Check firebird.log [no messages found for interval when this test was running]
Jump to: output_first_mismatch    outcomes_full_history    elapsed_time_chart
Show cross-report outcomes.

Annotation type Annotation details
2 @message
RecursionError: maximum recursion depth exceeded

LOG DETAILS:

2025-07-01 02:59:14.985
2025-07-01 02:59:14.991 act = <firebird.qa.plugin.Action object at [hex]>
2025-07-01 02:59:14.996
2025-07-01 02:59:15.001 @pytest.mark.version('>=3')
2025-07-01 02:59:15.005 def test_1(act: Action):
2025-07-01 02:59:15.010 act.expected_stdout = expected_stdout
2025-07-01 02:59:15.014 act.execute()
2025-07-01 02:59:15.019 >       assert act.clean_stdout == act.clean_expected_stdout
2025-07-01 02:59:15.028
2025-07-01 02:59:15.033 tests/bugs/core_2969_test.py:1211:
2025-07-01 02:59:15.037 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:15.042
2025-07-01 02:59:15.046 ops = ('==',), results = (False,)
2025-07-01 02:59:15.051 expls = ('%(py2)s\n{%(py2)s = %(py0)s.clean_stdout\n} == %(py6)s\n{%(py6)s = %(py4)s.clean_expected_stdout\n}',)
2025-07-01 02:59:15.056 each_obj = ('WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var...yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy')
2025-07-01 02:59:15.060
2025-07-01 02:59:15.065 def _call_reprcompare(
2025-07-01 02:59:15.070 ops: Sequence[str],
2025-07-01 02:59:15.075 results: Sequence[bool],
2025-07-01 02:59:15.080 expls: Sequence[str],
2025-07-01 02:59:15.084 each_obj: Sequence[object],
2025-07-01 02:59:15.088 ) -> str:
2025-07-01 02:59:15.093 for i, res, expl in zip(range(len(ops)), results, expls):
2025-07-01 02:59:15.098 try:
2025-07-01 02:59:15.103 done = not res
2025-07-01 02:59:15.107 except Exception:
2025-07-01 02:59:15.116 done = True
2025-07-01 02:59:15.120 if done:
2025-07-01 02:59:15.125 break
2025-07-01 02:59:15.129 if util._reprcompare is not None:
2025-07-01 02:59:15.134 >           custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1])
2025-07-01 02:59:15.138
2025-07-01 02:59:15.143 ../lib/python3.11/site-packages/_pytest/assertion/rewrite.py:499:
2025-07-01 02:59:15.148 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:15.152
2025-07-01 02:59:15.157 op = '=='
2025-07-01 02:59:15.162 left = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var_...    yyy\n1var_997                        yyy\n1var_998                        yyy\n1var_999                        yyy'
2025-07-01 02:59:15.167 right = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1 var... yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'
2025-07-01 02:59:15.172
2025-07-01 02:59:15.177 def callbinrepr(op, left: object, right: object) -> Optional[str]:
2025-07-01 02:59:15.182 """Call the pytest_assertrepr_compare hook and prepare the result.
2025-07-01 02:59:15.186
2025-07-01 02:59:15.192 This uses the first result from the hook and then ensures the
2025-07-01 02:59:15.199 following:
2025-07-01 02:59:15.206 * Overly verbose explanations are truncated unless configured otherwise
2025-07-01 02:59:15.213 (eg. if running in verbose mode).
2025-07-01 02:59:15.219 * Embedded newlines are escaped to help util.format_explanation()
2025-07-01 02:59:15.225 later.
2025-07-01 02:59:15.231 * If the rewrite mode is used embedded %-characters are replaced
2025-07-01 02:59:15.238 to protect later % formatting.
2025-07-01 02:59:15.244
2025-07-01 02:59:15.250 The result can be formatted by util.format_explanation() for
2025-07-01 02:59:15.256 pretty printing.
2025-07-01 02:59:15.261 """
2025-07-01 02:59:15.265 >       hook_result = ihook.pytest_assertrepr_compare(
2025-07-01 02:59:15.270 config=item.config, op=op, left=left, right=right
2025-07-01 02:59:15.274 )
2025-07-01 02:59:15.278
2025-07-01 02:59:15.283 ../lib/python3.11/site-packages/_pytest/assertion/__init__.py:141:
2025-07-01 02:59:15.288 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:15.293
2025-07-01 02:59:15.297 self = <HookCaller 'pytest_assertrepr_compare'>
2025-07-01 02:59:15.302 kwargs = {'config': <_pytest.config.Config object at [hex]>, 'left': 'WAS_OVERWRITTEN CTX_KEY                        C...yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'}
2025-07-01 02:59:15.306 firstresult = False
2025-07-01 02:59:15.311
2025-07-01 02:59:15.315 def __call__(self, **kwargs: object) -> Any:
2025-07-01 02:59:15.320 """Call the hook.
2025-07-01 02:59:15.324
2025-07-01 02:59:15.329 Only accepts keyword arguments, which should match the hook
2025-07-01 02:59:15.333 specification.
2025-07-01 02:59:15.337
2025-07-01 02:59:15.342 Returns the result(s) of calling all registered plugins, see
2025-07-01 02:59:15.347 :ref:`calling`.
2025-07-01 02:59:15.356 """
2025-07-01 02:59:15.361 assert (
2025-07-01 02:59:15.366 not self.is_historic()
2025-07-01 02:59:15.370 ), "Cannot directly call a historic hook - use call_historic instead."
2025-07-01 02:59:15.375 self._verify_all_args_are_provided(kwargs)
2025-07-01 02:59:15.379 firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
2025-07-01 02:59:15.384 >       return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
2025-07-01 02:59:15.388
2025-07-01 02:59:15.393 ../lib/python3.11/site-packages/pluggy/_hooks.py:493:
2025-07-01 02:59:15.397 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:15.401
2025-07-01 02:59:15.405 self = <_pytest.config.PytestPluginManager object at [hex]>
2025-07-01 02:59:15.410 hook_name = 'pytest_assertrepr_compare'
2025-07-01 02:59:15.415 methods = [<HookImpl plugin_name='assertion', plugin=<module '_pytest.assertion' from '/opt/distr/venv/lib/python3.11/site-packa...in_name='firebird', plugin=<module 'firebird.qa.plugin' from '/opt/distr/venv/firebird-qa/src/firebird/qa/plugin.py'>>]
2025-07-01 02:59:15.419 kwargs = {'config': <_pytest.config.Config object at [hex]>, 'left': 'WAS_OVERWRITTEN CTX_KEY                        C...yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'}
2025-07-01 02:59:15.423 firstresult = False
2025-07-01 02:59:15.427
2025-07-01 02:59:15.432 def _hookexec(
2025-07-01 02:59:15.436 self,
2025-07-01 02:59:15.440 hook_name: str,
2025-07-01 02:59:15.444 methods: Sequence[HookImpl],
2025-07-01 02:59:15.449 kwargs: Mapping[str, object],
2025-07-01 02:59:15.453 firstresult: bool,
2025-07-01 02:59:15.457 ) -> object | list[object]:
2025-07-01 02:59:15.461 # called from all hookcaller instances.
2025-07-01 02:59:15.467 # enable_tracing will set its own wrapping function at self._inner_hookexec
2025-07-01 02:59:15.473 >       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
2025-07-01 02:59:15.482
2025-07-01 02:59:15.491 ../lib/python3.11/site-packages/pluggy/_manager.py:115:
2025-07-01 02:59:15.501 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:15.505
2025-07-01 02:59:15.510 config = <_pytest.config.Config object at [hex]>, op = '=='
2025-07-01 02:59:15.514 left = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var_...    yyy\n1var_997                        yyy\n1var_998                        yyy\n1var_999                        yyy'
2025-07-01 02:59:15.519 right = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1 var... yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'
2025-07-01 02:59:15.524
2025-07-01 02:59:15.528 def pytest_assertrepr_compare(config: Config, op: str, left: object, right: object) -> Optional[List[str]]:
2025-07-01 02:59:15.533 """Returns explanation for comparisons in failing assert expressions.
2025-07-01 02:59:15.538
2025-07-01 02:59:15.543 If both objects are `str`, uses `difflib.ndiff` to provide explanation.
2025-07-01 02:59:15.548 """
2025-07-01 02:59:15.553 if isinstance(left, str) and isinstance(right, str) and op == "==":
2025-07-01 02:59:15.557 # 16.11.2023, pzotov: we have to put empty string at the beginning of each comparing lists.
2025-07-01 02:59:15.562 # Otherwise first diff will be at the same line as 'assert' phrase, which causes readability be poor.
2025-07-01 02:59:15.566 #
2025-07-01 02:59:15.570 left_lines = ['']
2025-07-01 02:59:15.575 left_lines.extend(left.splitlines())
2025-07-01 02:59:15.579 right_lines = ['']
2025-07-01 02:59:15.583 right_lines.extend(right.splitlines())
2025-07-01 02:59:15.588
2025-07-01 02:59:15.592 # 16.11.2023, pzotov
2025-07-01 02:59:15.601 # ndiff output must be interpreted as following:
2025-07-01 02:59:15.605 #     * "E     - <some text>" ==> MISSED line (it was in EXPECTED text but absent in actual one).
2025-07-01 02:59:15.610 #     * "E     + <some_text>" ==> EXCESSIVE line (it is not in EXPECTED text but did appear in actual).
2025-07-01 02:59:15.614 # But for QA-purposes, this output must answer the question:
2025-07-01 02:59:15.619 #     "what must be changed in ACTUAL output so that it became equal to EXPECTED"
2025-07-01 02:59:15.624 #     (i.e. how to "REVERT" actual back to expected).
2025-07-01 02:59:15.629 # In order to see such result, we have to specify 'right_lines' to the 1st argument that is passed to ndiff().
2025-07-01 02:59:15.634 # ::: NB :::
2025-07-01 02:59:15.638 # We assume that all tests are written so that ACTUAL output is left side in 'assert' statement and EXPECTED
2025-07-01 02:59:15.642 # is right side, e.g: assert act.clean_stdout == act.clean_expected_stdout
2025-07-01 02:59:15.647 # This requirement is CRUCIAL if we use ndiff() instead of default pytest comparison method!
2025-07-01 02:59:15.651 #
2025-07-01 02:59:15.655 >           return list(ndiff(right_lines, left_lines))
2025-07-01 02:59:15.660
2025-07-01 02:59:15.664 src/firebird/qa/plugin.py:608:
2025-07-01 02:59:15.668 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:15.672
2025-07-01 02:59:15.677 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:15.681 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:15.686 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:15.690
2025-07-01 02:59:15.696 def compare(self, a, b):
2025-07-01 02:59:15.704 r"""
2025-07-01 02:59:15.710 Compare two sequences of lines; generate the resulting delta.
2025-07-01 02:59:15.715
2025-07-01 02:59:15.724 Each sequence must contain individual single-line strings ending with
2025-07-01 02:59:15.729 newlines. Such sequences can be obtained from the `readlines()` method
2025-07-01 02:59:15.734 of file-like objects.  The delta generated also consists of newline-
2025-07-01 02:59:15.739 terminated strings, ready to be printed as-is via the writelines()
2025-07-01 02:59:15.743 method of a file-like object.
2025-07-01 02:59:15.748
2025-07-01 02:59:15.752 Example:
2025-07-01 02:59:15.756
2025-07-01 02:59:15.761 >>> print(''.join(Differ().compare('one\ntwo\nthree\n'.splitlines(True),
2025-07-01 02:59:15.766 ...                                'ore\ntree\nemu\n'.splitlines(True))),
2025-07-01 02:59:15.770 ...       end="")
2025-07-01 02:59:15.775 - one
2025-07-01 02:59:15.785 + ore
2025-07-01 02:59:15.793 - two
2025-07-01 02:59:15.797 - three
2025-07-01 02:59:15.806 + tree
2025-07-01 02:59:15.810 + emu
2025-07-01 02:59:15.814 """
2025-07-01 02:59:15.819
2025-07-01 02:59:15.823 cruncher = SequenceMatcher(self.linejunk, a, b)
2025-07-01 02:59:15.828 for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
2025-07-01 02:59:15.832 if tag == 'replace':
2025-07-01 02:59:15.836 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:15.841 elif tag == 'delete':
2025-07-01 02:59:15.845 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:15.850 elif tag == 'insert':
2025-07-01 02:59:15.854 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:15.858 elif tag == 'equal':
2025-07-01 02:59:15.863 g = self._dump(' ', a, alo, ahi)
2025-07-01 02:59:15.867 else:
2025-07-01 02:59:15.871 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:15.876
2025-07-01 02:59:15.880 >           yield from g
2025-07-01 02:59:15.884
2025-07-01 02:59:15.889 /usr/lib/python3.11/difflib.py:872:
2025-07-01 02:59:15.893 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:15.898
2025-07-01 02:59:15.902 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:15.907 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:15.912 alo = 3, ahi = 1101
2025-07-01 02:59:15.917 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:15.922 blo = 3, bhi = 1101
2025-07-01 02:59:15.926
2025-07-01 02:59:15.930 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:15.934 r"""
2025-07-01 02:59:15.939 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:15.943 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:15.950 synch point, and intraline difference marking is done on the
2025-07-01 02:59:15.956 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:15.960
2025-07-01 02:59:15.965 Example:
2025-07-01 02:59:15.969
2025-07-01 02:59:15.973 >>> d = Differ()
2025-07-01 02:59:15.978 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:15.983 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:15.988 >>> print(''.join(results), end="")
2025-07-01 02:59:15.992 - abcDefghiJkl
2025-07-01 02:59:16.001 + abcdefGhijkl
2025-07-01 02:59:16.010 """
2025-07-01 02:59:16.014
2025-07-01 02:59:16.020 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:16.026 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:16.031 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:16.036 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:16.041 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:16.045
2025-07-01 02:59:16.050 # search for the pair that matches best without being identical
2025-07-01 02:59:16.056 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:16.061 # on junk -- unless we have to)
2025-07-01 02:59:16.066 for j in range(blo, bhi):
2025-07-01 02:59:16.071 bj = b[j]
2025-07-01 02:59:16.077 cruncher.set_seq2(bj)
2025-07-01 02:59:16.082 for i in range(alo, ahi):
2025-07-01 02:59:16.087 ai = a[i]
2025-07-01 02:59:16.092 if ai == bj:
2025-07-01 02:59:16.098 if eqi is None:
2025-07-01 02:59:16.102 eqi, eqj = i, j
2025-07-01 02:59:16.107 continue
2025-07-01 02:59:16.111 cruncher.set_seq1(ai)
2025-07-01 02:59:16.116 # computing similarity is expensive, so use the quick
2025-07-01 02:59:16.121 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:16.126 # compares by a factor of 3.
2025-07-01 02:59:16.131 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:16.136 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:16.140 # of the computation is cached by cruncher
2025-07-01 02:59:16.145 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:16.149 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:16.154 cruncher.ratio() > best_ratio:
2025-07-01 02:59:16.158 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:16.163 if best_ratio < cutoff:
2025-07-01 02:59:16.167 # no non-identical "pretty close" pair
2025-07-01 02:59:16.172 if eqi is None:
2025-07-01 02:59:16.176 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:16.183 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:16.187 return
2025-07-01 02:59:16.192 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:16.196 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:16.200 else:
2025-07-01 02:59:16.205 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:16.209 eqi = None
2025-07-01 02:59:16.213
2025-07-01 02:59:16.218 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:16.224 # identical
2025-07-01 02:59:16.229
2025-07-01 02:59:16.233 # pump out diffs from before the synch point
2025-07-01 02:59:16.237 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:16.242
2025-07-01 02:59:16.247 # do intraline marking on the synch pair
2025-07-01 02:59:16.251 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:16.255 if eqi is None:
2025-07-01 02:59:16.260 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:16.264 atags = btags = ""
2025-07-01 02:59:16.268 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:16.272 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:16.277 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:16.281 if tag == 'replace':
2025-07-01 02:59:16.286 atags += '^' * la
2025-07-01 02:59:16.290 btags += '^' * lb
2025-07-01 02:59:16.295 elif tag == 'delete':
2025-07-01 02:59:16.299 atags += '-' * la
2025-07-01 02:59:16.303 elif tag == 'insert':
2025-07-01 02:59:16.308 btags += '+' * lb
2025-07-01 02:59:16.312 elif tag == 'equal':
2025-07-01 02:59:16.316 atags += ' ' * la
2025-07-01 02:59:16.320 btags += ' ' * lb
2025-07-01 02:59:16.324 else:
2025-07-01 02:59:16.329 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:16.334 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:16.339 else:
2025-07-01 02:59:16.343 # the synch pair is identical
2025-07-01 02:59:16.348 yield '  ' + aelt
2025-07-01 02:59:16.352
2025-07-01 02:59:16.357 # pump out diffs from after the synch point
2025-07-01 02:59:16.361 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:16.370
2025-07-01 02:59:16.379 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:16.384 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:16.388
2025-07-01 02:59:16.393 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:16.398 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:16.403 alo = 4, ahi = 1101
2025-07-01 02:59:16.408 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:16.413 blo = 4, bhi = 1101
2025-07-01 02:59:16.417
2025-07-01 02:59:16.422 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:16.427 g = []
2025-07-01 02:59:16.431 if alo < ahi:
2025-07-01 02:59:16.436 if blo < bhi:
2025-07-01 02:59:16.442 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:16.446 else:
2025-07-01 02:59:16.451 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:16.455 elif blo < bhi:
2025-07-01 02:59:16.460 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:16.464
2025-07-01 02:59:16.469 >       yield from g
2025-07-01 02:59:16.473
2025-07-01 02:59:16.478 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:16.483 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:16.487
2025-07-01 02:59:16.492 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:16.498 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:16.502 alo = 4, ahi = 1101
2025-07-01 02:59:16.507 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:16.512 blo = 4, bhi = 1101
2025-07-01 02:59:16.517
2025-07-01 02:59:16.521 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:16.526 r"""
2025-07-01 02:59:16.531 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:16.536 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:16.540 synch point, and intraline difference marking is done on the
2025-07-01 02:59:16.545 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:16.549
2025-07-01 02:59:16.554 Example:
2025-07-01 02:59:16.559
2025-07-01 02:59:16.563 >>> d = Differ()
2025-07-01 02:59:16.568 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:16.573 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:16.578 >>> print(''.join(results), end="")
2025-07-01 02:59:16.583 - abcDefghiJkl
2025-07-01 02:59:16.594 + abcdefGhijkl
2025-07-01 02:59:16.605 """
2025-07-01 02:59:16.611
2025-07-01 02:59:16.617 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:16.623 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:16.627 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:16.632 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:16.637 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:16.642
2025-07-01 02:59:16.647 # search for the pair that matches best without being identical
2025-07-01 02:59:16.652 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:16.656 # on junk -- unless we have to)
2025-07-01 02:59:16.661 for j in range(blo, bhi):
2025-07-01 02:59:16.666 bj = b[j]
2025-07-01 02:59:16.671 cruncher.set_seq2(bj)
2025-07-01 02:59:16.676 for i in range(alo, ahi):
2025-07-01 02:59:16.681 ai = a[i]
2025-07-01 02:59:16.685 if ai == bj:
2025-07-01 02:59:16.690 if eqi is None:
2025-07-01 02:59:16.695 eqi, eqj = i, j
2025-07-01 02:59:16.699 continue
2025-07-01 02:59:16.704 cruncher.set_seq1(ai)
2025-07-01 02:59:16.708 # computing similarity is expensive, so use the quick
2025-07-01 02:59:16.713 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:16.717 # compares by a factor of 3.
2025-07-01 02:59:16.722 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:16.728 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:16.733 # of the computation is cached by cruncher
2025-07-01 02:59:16.737 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:16.742 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:16.746 cruncher.ratio() > best_ratio:
2025-07-01 02:59:16.751 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:16.755 if best_ratio < cutoff:
2025-07-01 02:59:16.760 # no non-identical "pretty close" pair
2025-07-01 02:59:16.764 if eqi is None:
2025-07-01 02:59:16.768 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:16.773 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:16.777 return
2025-07-01 02:59:16.782 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:16.786 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:16.790 else:
2025-07-01 02:59:16.795 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:16.799 eqi = None
2025-07-01 02:59:16.804
2025-07-01 02:59:16.809 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:16.813 # identical
2025-07-01 02:59:16.818
2025-07-01 02:59:16.823 # pump out diffs from before the synch point
2025-07-01 02:59:16.828 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:16.832
2025-07-01 02:59:16.837 # do intraline marking on the synch pair
2025-07-01 02:59:16.841 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:16.846 if eqi is None:
2025-07-01 02:59:16.850 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:16.855 atags = btags = ""
2025-07-01 02:59:16.859 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:16.863 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:16.868 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:16.872 if tag == 'replace':
2025-07-01 02:59:16.877 atags += '^' * la
2025-07-01 02:59:16.881 btags += '^' * lb
2025-07-01 02:59:16.885 elif tag == 'delete':
2025-07-01 02:59:16.890 atags += '-' * la
2025-07-01 02:59:16.894 elif tag == 'insert':
2025-07-01 02:59:16.898 btags += '+' * lb
2025-07-01 02:59:16.902 elif tag == 'equal':
2025-07-01 02:59:16.907 atags += ' ' * la
2025-07-01 02:59:16.911 btags += ' ' * lb
2025-07-01 02:59:16.915 else:
2025-07-01 02:59:16.920 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:16.924 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:16.929 else:
2025-07-01 02:59:16.933 # the synch pair is identical
2025-07-01 02:59:16.937 yield '  ' + aelt
2025-07-01 02:59:16.941
2025-07-01 02:59:16.946 # pump out diffs from after the synch point
2025-07-01 02:59:16.950 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:16.954
2025-07-01 02:59:16.959 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:16.963 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:16.968
2025-07-01 02:59:16.973 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:16.977 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:16.982 alo = 5, ahi = 1101
2025-07-01 02:59:16.987 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:16.991 blo = 5, bhi = 1101
2025-07-01 02:59:16.995
2025-07-01 02:59:17.000 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:17.004 g = []
2025-07-01 02:59:17.009 if alo < ahi:
2025-07-01 02:59:17.013 if blo < bhi:
2025-07-01 02:59:17.018 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:17.023 else:
2025-07-01 02:59:17.027 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:17.031 elif blo < bhi:
2025-07-01 02:59:17.036 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:17.040
2025-07-01 02:59:17.045 >       yield from g
2025-07-01 02:59:17.049
2025-07-01 02:59:17.054 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:17.058 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:17.063
2025-07-01 02:59:17.067 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:17.073 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:17.077 alo = 5, ahi = 1101
2025-07-01 02:59:17.082 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:17.087 blo = 5, bhi = 1101
2025-07-01 02:59:17.091
2025-07-01 02:59:17.095 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:17.100 r"""
2025-07-01 02:59:17.104 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:17.109 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:17.113 synch point, and intraline difference marking is done on the
2025-07-01 02:59:17.118 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:17.123
2025-07-01 02:59:17.127 Example:
2025-07-01 02:59:17.132
2025-07-01 02:59:17.137 >>> d = Differ()
2025-07-01 02:59:17.141 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:17.146 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:17.150 >>> print(''.join(results), end="")
2025-07-01 02:59:17.154 - abcDefghiJkl
2025-07-01 02:59:17.163 + abcdefGhijkl
2025-07-01 02:59:17.172 """
2025-07-01 02:59:17.177
2025-07-01 02:59:17.182 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:17.187 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:17.192 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:17.196 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:17.201 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:17.205
2025-07-01 02:59:17.209 # search for the pair that matches best without being identical
2025-07-01 02:59:17.214 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:17.218 # on junk -- unless we have to)
2025-07-01 02:59:17.223 for j in range(blo, bhi):
2025-07-01 02:59:17.228 bj = b[j]
2025-07-01 02:59:17.233 cruncher.set_seq2(bj)
2025-07-01 02:59:17.237 for i in range(alo, ahi):
2025-07-01 02:59:17.241 ai = a[i]
2025-07-01 02:59:17.246 if ai == bj:
2025-07-01 02:59:17.251 if eqi is None:
2025-07-01 02:59:17.255 eqi, eqj = i, j
2025-07-01 02:59:17.259 continue
2025-07-01 02:59:17.264 cruncher.set_seq1(ai)
2025-07-01 02:59:17.269 # computing similarity is expensive, so use the quick
2025-07-01 02:59:17.273 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:17.278 # compares by a factor of 3.
2025-07-01 02:59:17.283 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:17.288 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:17.292 # of the computation is cached by cruncher
2025-07-01 02:59:17.297 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:17.301 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:17.306 cruncher.ratio() > best_ratio:
2025-07-01 02:59:17.310 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:17.315 if best_ratio < cutoff:
2025-07-01 02:59:17.319 # no non-identical "pretty close" pair
2025-07-01 02:59:17.324 if eqi is None:
2025-07-01 02:59:17.328 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:17.333 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:17.338 return
2025-07-01 02:59:17.342 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:17.347 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:17.351 else:
2025-07-01 02:59:17.356 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:17.360 eqi = None
2025-07-01 02:59:17.366
2025-07-01 02:59:17.372 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:17.379 # identical
2025-07-01 02:59:17.386
2025-07-01 02:59:17.393 # pump out diffs from before the synch point
2025-07-01 02:59:17.400 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:17.406
2025-07-01 02:59:17.412 # do intraline marking on the synch pair
2025-07-01 02:59:17.417 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:17.422 if eqi is None:
2025-07-01 02:59:17.427 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:17.432 atags = btags = ""
2025-07-01 02:59:17.436 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:17.441 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:17.446 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:17.450 if tag == 'replace':
2025-07-01 02:59:17.455 atags += '^' * la
2025-07-01 02:59:17.459 btags += '^' * lb
2025-07-01 02:59:17.464 elif tag == 'delete':
2025-07-01 02:59:17.468 atags += '-' * la
2025-07-01 02:59:17.473 elif tag == 'insert':
2025-07-01 02:59:17.478 btags += '+' * lb
2025-07-01 02:59:17.483 elif tag == 'equal':
2025-07-01 02:59:17.487 atags += ' ' * la
2025-07-01 02:59:17.492 btags += ' ' * lb
2025-07-01 02:59:17.496 else:
2025-07-01 02:59:17.501 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:17.506 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:17.511 else:
2025-07-01 02:59:17.516 # the synch pair is identical
2025-07-01 02:59:17.522 yield '  ' + aelt
2025-07-01 02:59:17.527
2025-07-01 02:59:17.531 # pump out diffs from after the synch point
2025-07-01 02:59:17.536 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:17.541
2025-07-01 02:59:17.545 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:17.550 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:17.554
2025-07-01 02:59:17.559 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:17.564 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:17.569 alo = 6, ahi = 1101
2025-07-01 02:59:17.574 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:17.578 blo = 6, bhi = 1101
2025-07-01 02:59:17.585
2025-07-01 02:59:17.590 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:17.594 g = []
2025-07-01 02:59:17.599 if alo < ahi:
2025-07-01 02:59:17.603 if blo < bhi:
2025-07-01 02:59:17.608 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:17.613 else:
2025-07-01 02:59:17.617 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:17.622 elif blo < bhi:
2025-07-01 02:59:17.627 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:17.632
2025-07-01 02:59:17.638 >       yield from g
2025-07-01 02:59:17.643
2025-07-01 02:59:17.649 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:17.655 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:17.660
2025-07-01 02:59:17.665 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:17.670 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:17.675 alo = 6, ahi = 1101
2025-07-01 02:59:17.681 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:17.686 blo = 6, bhi = 1101
2025-07-01 02:59:17.691
2025-07-01 02:59:17.696 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:17.701 r"""
2025-07-01 02:59:17.706 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:17.712 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:17.717 synch point, and intraline difference marking is done on the
2025-07-01 02:59:17.721 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:17.726
2025-07-01 02:59:17.730 Example:
2025-07-01 02:59:17.735
2025-07-01 02:59:17.739 >>> d = Differ()
2025-07-01 02:59:17.744 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:17.748 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:17.753 >>> print(''.join(results), end="")
2025-07-01 02:59:17.757 - abcDefghiJkl
2025-07-01 02:59:17.766 + abcdefGhijkl
2025-07-01 02:59:17.782 """
2025-07-01 02:59:17.792
2025-07-01 02:59:17.798 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:17.804 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:17.810 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:17.814 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:17.819 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:17.823
2025-07-01 02:59:17.828 # search for the pair that matches best without being identical
2025-07-01 02:59:17.832 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:17.837 # on junk -- unless we have to)
2025-07-01 02:59:17.842 for j in range(blo, bhi):
2025-07-01 02:59:17.846 bj = b[j]
2025-07-01 02:59:17.850 cruncher.set_seq2(bj)
2025-07-01 02:59:17.855 for i in range(alo, ahi):
2025-07-01 02:59:17.860 ai = a[i]
2025-07-01 02:59:17.865 if ai == bj:
2025-07-01 02:59:17.869 if eqi is None:
2025-07-01 02:59:17.874 eqi, eqj = i, j
2025-07-01 02:59:17.878 continue
2025-07-01 02:59:17.883 cruncher.set_seq1(ai)
2025-07-01 02:59:17.887 # computing similarity is expensive, so use the quick
2025-07-01 02:59:17.892 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:17.897 # compares by a factor of 3.
2025-07-01 02:59:17.901 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:17.906 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:17.911 # of the computation is cached by cruncher
2025-07-01 02:59:17.915 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:17.919 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:17.924 cruncher.ratio() > best_ratio:
2025-07-01 02:59:17.928 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:17.933 if best_ratio < cutoff:
2025-07-01 02:59:17.937 # no non-identical "pretty close" pair
2025-07-01 02:59:17.942 if eqi is None:
2025-07-01 02:59:17.946 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:17.951 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:17.955 return
2025-07-01 02:59:17.959 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:17.964 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:17.969 else:
2025-07-01 02:59:17.973 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:17.977 eqi = None
2025-07-01 02:59:17.982
2025-07-01 02:59:17.986 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:17.991 # identical
2025-07-01 02:59:17.995
2025-07-01 02:59:17.999 # pump out diffs from before the synch point
2025-07-01 02:59:18.004 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:18.009
2025-07-01 02:59:18.014 # do intraline marking on the synch pair
2025-07-01 02:59:18.018 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:18.023 if eqi is None:
2025-07-01 02:59:18.027 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:18.031 atags = btags = ""
2025-07-01 02:59:18.036 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:18.040 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:18.045 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:18.049 if tag == 'replace':
2025-07-01 02:59:18.054 atags += '^' * la
2025-07-01 02:59:18.058 btags += '^' * lb
2025-07-01 02:59:18.063 elif tag == 'delete':
2025-07-01 02:59:18.067 atags += '-' * la
2025-07-01 02:59:18.071 elif tag == 'insert':
2025-07-01 02:59:18.076 btags += '+' * lb
2025-07-01 02:59:18.081 elif tag == 'equal':
2025-07-01 02:59:18.085 atags += ' ' * la
2025-07-01 02:59:18.089 btags += ' ' * lb
2025-07-01 02:59:18.094 else:
2025-07-01 02:59:18.098 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:18.103 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:18.107 else:
2025-07-01 02:59:18.111 # the synch pair is identical
2025-07-01 02:59:18.116 yield '  ' + aelt
2025-07-01 02:59:18.120
2025-07-01 02:59:18.125 # pump out diffs from after the synch point
2025-07-01 02:59:18.129 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:18.133
2025-07-01 02:59:18.138 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:18.142 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:18.148
2025-07-01 02:59:18.152 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:18.158 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:18.162 alo = 7, ahi = 1101
2025-07-01 02:59:18.167 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:18.171 blo = 7, bhi = 1101
2025-07-01 02:59:18.175
2025-07-01 02:59:18.179 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:18.184 g = []
2025-07-01 02:59:18.188 if alo < ahi:
2025-07-01 02:59:18.192 if blo < bhi:
2025-07-01 02:59:18.197 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:18.201 else:
2025-07-01 02:59:18.205 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:18.210 elif blo < bhi:
2025-07-01 02:59:18.214 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:18.218
2025-07-01 02:59:18.222 >       yield from g
2025-07-01 02:59:18.227
2025-07-01 02:59:18.231 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:18.236 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:18.240
2025-07-01 02:59:18.244 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:18.249 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:18.253 alo = 7, ahi = 1101
2025-07-01 02:59:18.258 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:18.262 blo = 7, bhi = 1101
2025-07-01 02:59:18.267
2025-07-01 02:59:18.271 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:18.275 r"""
2025-07-01 02:59:18.280 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:18.284 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:18.288 synch point, and intraline difference marking is done on the
2025-07-01 02:59:18.293 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:18.297
2025-07-01 02:59:18.301 Example:
2025-07-01 02:59:18.305
2025-07-01 02:59:18.310 >>> d = Differ()
2025-07-01 02:59:18.314 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:18.318 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:18.322 >>> print(''.join(results), end="")
2025-07-01 02:59:18.327 - abcDefghiJkl
2025-07-01 02:59:18.335 + abcdefGhijkl
2025-07-01 02:59:18.343 """
2025-07-01 02:59:18.348
2025-07-01 02:59:18.352 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:18.357 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:18.361 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:18.366 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:18.370 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:18.375
2025-07-01 02:59:18.379 # search for the pair that matches best without being identical
2025-07-01 02:59:18.383 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:18.388 # on junk -- unless we have to)
2025-07-01 02:59:18.392 for j in range(blo, bhi):
2025-07-01 02:59:18.397 bj = b[j]
2025-07-01 02:59:18.401 cruncher.set_seq2(bj)
2025-07-01 02:59:18.405 for i in range(alo, ahi):
2025-07-01 02:59:18.410 ai = a[i]
2025-07-01 02:59:18.414 if ai == bj:
2025-07-01 02:59:18.418 if eqi is None:
2025-07-01 02:59:18.423 eqi, eqj = i, j
2025-07-01 02:59:18.427 continue
2025-07-01 02:59:18.431 cruncher.set_seq1(ai)
2025-07-01 02:59:18.436 # computing similarity is expensive, so use the quick
2025-07-01 02:59:18.440 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:18.445 # compares by a factor of 3.
2025-07-01 02:59:18.449 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:18.454 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:18.458 # of the computation is cached by cruncher
2025-07-01 02:59:18.462 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:18.467 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:18.471 cruncher.ratio() > best_ratio:
2025-07-01 02:59:18.475 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:18.480 if best_ratio < cutoff:
2025-07-01 02:59:18.484 # no non-identical "pretty close" pair
2025-07-01 02:59:18.488 if eqi is None:
2025-07-01 02:59:18.493 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:18.497 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:18.501 return
2025-07-01 02:59:18.506 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:18.510 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:18.514 else:
2025-07-01 02:59:18.519 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:18.523 eqi = None
2025-07-01 02:59:18.527
2025-07-01 02:59:18.532 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:18.536 # identical
2025-07-01 02:59:18.540
2025-07-01 02:59:18.544 # pump out diffs from before the synch point
2025-07-01 02:59:18.549 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:18.553
2025-07-01 02:59:18.557 # do intraline marking on the synch pair
2025-07-01 02:59:18.561 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:18.566 if eqi is None:
2025-07-01 02:59:18.570 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:18.575 atags = btags = ""
2025-07-01 02:59:18.579 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:18.584 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:18.588 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:18.592 if tag == 'replace':
2025-07-01 02:59:18.597 atags += '^' * la
2025-07-01 02:59:18.601 btags += '^' * lb
2025-07-01 02:59:18.605 elif tag == 'delete':
2025-07-01 02:59:18.610 atags += '-' * la
2025-07-01 02:59:18.614 elif tag == 'insert':
2025-07-01 02:59:18.618 btags += '+' * lb
2025-07-01 02:59:18.622 elif tag == 'equal':
2025-07-01 02:59:18.627 atags += ' ' * la
2025-07-01 02:59:18.631 btags += ' ' * lb
2025-07-01 02:59:18.635 else:
2025-07-01 02:59:18.640 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:18.644 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:18.648 else:
2025-07-01 02:59:18.653 # the synch pair is identical
2025-07-01 02:59:18.657 yield '  ' + aelt
2025-07-01 02:59:18.661
2025-07-01 02:59:18.665 # pump out diffs from after the synch point
2025-07-01 02:59:18.670 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:18.674
2025-07-01 02:59:18.679 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:18.683 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:18.687
2025-07-01 02:59:18.692 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:18.696 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:18.701 alo = 8, ahi = 1101
2025-07-01 02:59:18.705 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:18.709 blo = 8, bhi = 1101
2025-07-01 02:59:18.713
2025-07-01 02:59:18.718 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:18.722 g = []
2025-07-01 02:59:18.726 if alo < ahi:
2025-07-01 02:59:18.732 if blo < bhi:
2025-07-01 02:59:18.737 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:18.741 else:
2025-07-01 02:59:18.745 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:18.750 elif blo < bhi:
2025-07-01 02:59:18.754 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:18.758
2025-07-01 02:59:18.762 >       yield from g
2025-07-01 02:59:18.766
2025-07-01 02:59:18.771 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:18.775 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:18.779
2025-07-01 02:59:18.784 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:18.789 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:18.793 alo = 8, ahi = 1101
2025-07-01 02:59:18.798 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:18.802 blo = 8, bhi = 1101
2025-07-01 02:59:18.806
2025-07-01 02:59:18.810 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:18.815 r"""
2025-07-01 02:59:18.819 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:18.823 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:18.828 synch point, and intraline difference marking is done on the
2025-07-01 02:59:18.832 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:18.836
2025-07-01 02:59:18.841 Example:
2025-07-01 02:59:18.845
2025-07-01 02:59:18.849 >>> d = Differ()
2025-07-01 02:59:18.853 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:18.858 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:18.862 >>> print(''.join(results), end="")
2025-07-01 02:59:18.866 - abcDefghiJkl
2025-07-01 02:59:18.875 + abcdefGhijkl
2025-07-01 02:59:18.884 """
2025-07-01 02:59:18.888
2025-07-01 02:59:18.892 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:18.896 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:18.901 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:18.905 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:18.910 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:18.914
2025-07-01 02:59:18.918 # search for the pair that matches best without being identical
2025-07-01 02:59:18.923 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:18.927 # on junk -- unless we have to)
2025-07-01 02:59:18.932 for j in range(blo, bhi):
2025-07-01 02:59:18.937 bj = b[j]
2025-07-01 02:59:18.941 cruncher.set_seq2(bj)
2025-07-01 02:59:18.946 for i in range(alo, ahi):
2025-07-01 02:59:18.950 ai = a[i]
2025-07-01 02:59:18.955 if ai == bj:
2025-07-01 02:59:18.960 if eqi is None:
2025-07-01 02:59:18.964 eqi, eqj = i, j
2025-07-01 02:59:18.968 continue
2025-07-01 02:59:18.973 cruncher.set_seq1(ai)
2025-07-01 02:59:18.977 # computing similarity is expensive, so use the quick
2025-07-01 02:59:18.982 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:18.987 # compares by a factor of 3.
2025-07-01 02:59:18.991 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:18.996 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:19.000 # of the computation is cached by cruncher
2025-07-01 02:59:19.005 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:19.010 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:19.014 cruncher.ratio() > best_ratio:
2025-07-01 02:59:19.019 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:19.023 if best_ratio < cutoff:
2025-07-01 02:59:19.027 # no non-identical "pretty close" pair
2025-07-01 02:59:19.032 if eqi is None:
2025-07-01 02:59:19.036 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:19.040 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:19.045 return
2025-07-01 02:59:19.049 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:19.053 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:19.058 else:
2025-07-01 02:59:19.062 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:19.067 eqi = None
2025-07-01 02:59:19.071
2025-07-01 02:59:19.076 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:19.080 # identical
2025-07-01 02:59:19.084
2025-07-01 02:59:19.089 # pump out diffs from before the synch point
2025-07-01 02:59:19.093 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:19.098
2025-07-01 02:59:19.102 # do intraline marking on the synch pair
2025-07-01 02:59:19.106 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:19.111 if eqi is None:
2025-07-01 02:59:19.115 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:19.119 atags = btags = ""
2025-07-01 02:59:19.124 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:19.128 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:19.133 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:19.137 if tag == 'replace':
2025-07-01 02:59:19.141 atags += '^' * la
2025-07-01 02:59:19.146 btags += '^' * lb
2025-07-01 02:59:19.150 elif tag == 'delete':
2025-07-01 02:59:19.154 atags += '-' * la
2025-07-01 02:59:19.159 elif tag == 'insert':
2025-07-01 02:59:19.163 btags += '+' * lb
2025-07-01 02:59:19.167 elif tag == 'equal':
2025-07-01 02:59:19.172 atags += ' ' * la
2025-07-01 02:59:19.176 btags += ' ' * lb
2025-07-01 02:59:19.182 else:
2025-07-01 02:59:19.186 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:19.191 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:19.195 else:
2025-07-01 02:59:19.200 # the synch pair is identical
2025-07-01 02:59:19.204 yield '  ' + aelt
2025-07-01 02:59:19.209
2025-07-01 02:59:19.213 # pump out diffs from after the synch point
2025-07-01 02:59:19.218 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:19.222
2025-07-01 02:59:19.227 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:19.232 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:19.237
2025-07-01 02:59:19.241 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:19.246 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:19.250 alo = 9, ahi = 1101
2025-07-01 02:59:19.256 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:19.260 blo = 9, bhi = 1101
2025-07-01 02:59:19.264
2025-07-01 02:59:19.268 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:19.273 g = []
2025-07-01 02:59:19.277 if alo < ahi:
2025-07-01 02:59:19.281 if blo < bhi:
2025-07-01 02:59:19.286 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:19.291 else:
2025-07-01 02:59:19.297 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:19.302 elif blo < bhi:
2025-07-01 02:59:19.307 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:19.311
2025-07-01 02:59:19.315 >       yield from g
2025-07-01 02:59:19.320
2025-07-01 02:59:19.324 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:19.329 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:19.333
2025-07-01 02:59:19.338 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:19.343 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:19.347 alo = 9, ahi = 1101
2025-07-01 02:59:19.353 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:19.358 blo = 9, bhi = 1101
2025-07-01 02:59:19.363
2025-07-01 02:59:19.367 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:19.372 r"""
2025-07-01 02:59:19.376 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:19.381 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:19.385 synch point, and intraline difference marking is done on the
2025-07-01 02:59:19.390 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:19.395
2025-07-01 02:59:19.399 Example:
2025-07-01 02:59:19.403
2025-07-01 02:59:19.408 >>> d = Differ()
2025-07-01 02:59:19.413 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:19.417 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:19.422 >>> print(''.join(results), end="")
2025-07-01 02:59:19.426 - abcDefghiJkl
2025-07-01 02:59:19.435 + abcdefGhijkl
2025-07-01 02:59:19.445 """
2025-07-01 02:59:19.449
2025-07-01 02:59:19.454 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:19.459 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:19.464 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:19.468 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:19.473 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:19.477
2025-07-01 02:59:19.482 # search for the pair that matches best without being identical
2025-07-01 02:59:19.486 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:19.491 # on junk -- unless we have to)
2025-07-01 02:59:19.495 for j in range(blo, bhi):
2025-07-01 02:59:19.500 bj = b[j]
2025-07-01 02:59:19.505 cruncher.set_seq2(bj)
2025-07-01 02:59:19.510 for i in range(alo, ahi):
2025-07-01 02:59:19.515 ai = a[i]
2025-07-01 02:59:19.519 if ai == bj:
2025-07-01 02:59:19.524 if eqi is None:
2025-07-01 02:59:19.528 eqi, eqj = i, j
2025-07-01 02:59:19.533 continue
2025-07-01 02:59:19.537 cruncher.set_seq1(ai)
2025-07-01 02:59:19.542 # computing similarity is expensive, so use the quick
2025-07-01 02:59:19.546 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:19.551 # compares by a factor of 3.
2025-07-01 02:59:19.556 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:19.561 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:19.565 # of the computation is cached by cruncher
2025-07-01 02:59:19.570 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:19.575 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:19.580 cruncher.ratio() > best_ratio:
2025-07-01 02:59:19.584 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:19.588 if best_ratio < cutoff:
2025-07-01 02:59:19.593 # no non-identical "pretty close" pair
2025-07-01 02:59:19.597 if eqi is None:
2025-07-01 02:59:19.602 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:19.607 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:19.612 return
2025-07-01 02:59:19.617 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:19.621 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:19.626 else:
2025-07-01 02:59:19.630 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:19.635 eqi = None
2025-07-01 02:59:19.639
2025-07-01 02:59:19.644 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:19.649 # identical
2025-07-01 02:59:19.654
2025-07-01 02:59:19.658 # pump out diffs from before the synch point
2025-07-01 02:59:19.663 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:19.668
2025-07-01 02:59:19.672 # do intraline marking on the synch pair
2025-07-01 02:59:19.676 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:19.682 if eqi is None:
2025-07-01 02:59:19.687 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:19.691 atags = btags = ""
2025-07-01 02:59:19.696 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:19.701 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:19.705 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:19.710 if tag == 'replace':
2025-07-01 02:59:19.715 atags += '^' * la
2025-07-01 02:59:19.720 btags += '^' * lb
2025-07-01 02:59:19.724 elif tag == 'delete':
2025-07-01 02:59:19.729 atags += '-' * la
2025-07-01 02:59:19.733 elif tag == 'insert':
2025-07-01 02:59:19.738 btags += '+' * lb
2025-07-01 02:59:19.742 elif tag == 'equal':
2025-07-01 02:59:19.747 atags += ' ' * la
2025-07-01 02:59:19.751 btags += ' ' * lb
2025-07-01 02:59:19.755 else:
2025-07-01 02:59:19.760 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:19.765 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:19.770 else:
2025-07-01 02:59:19.774 # the synch pair is identical
2025-07-01 02:59:19.779 yield '  ' + aelt
2025-07-01 02:59:19.783
2025-07-01 02:59:19.787 # pump out diffs from after the synch point
2025-07-01 02:59:19.792 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:19.797
2025-07-01 02:59:19.802 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:19.807 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:19.812
2025-07-01 02:59:19.817 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:19.823 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:19.827 alo = 10, ahi = 1101
2025-07-01 02:59:19.833 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:19.838 blo = 10, bhi = 1101
2025-07-01 02:59:19.842
2025-07-01 02:59:19.847 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:19.852 g = []
2025-07-01 02:59:19.857 if alo < ahi:
2025-07-01 02:59:19.861 if blo < bhi:
2025-07-01 02:59:19.866 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:19.870 else:
2025-07-01 02:59:19.875 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:19.879 elif blo < bhi:
2025-07-01 02:59:19.884 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:19.888
2025-07-01 02:59:19.892 >       yield from g
2025-07-01 02:59:19.897
2025-07-01 02:59:19.902 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:19.907 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:19.911
2025-07-01 02:59:19.917 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:19.922 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:19.927 alo = 10, ahi = 1101
2025-07-01 02:59:19.932 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:19.936 blo = 10, bhi = 1101
2025-07-01 02:59:19.940
2025-07-01 02:59:19.945 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:19.950 r"""
2025-07-01 02:59:19.954 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:19.964 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:19.969 synch point, and intraline difference marking is done on the
2025-07-01 02:59:19.974 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:19.978
2025-07-01 02:59:19.982 Example:
2025-07-01 02:59:19.986
2025-07-01 02:59:19.991 >>> d = Differ()
2025-07-01 02:59:19.995 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:19.999 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:20.004 >>> print(''.join(results), end="")
2025-07-01 02:59:20.009 - abcDefghiJkl
2025-07-01 02:59:20.019 + abcdefGhijkl
2025-07-01 02:59:20.028 """
2025-07-01 02:59:20.034
2025-07-01 02:59:20.039 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:20.043 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:20.048 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:20.053 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:20.058 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:20.062
2025-07-01 02:59:20.067 # search for the pair that matches best without being identical
2025-07-01 02:59:20.072 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:20.078 # on junk -- unless we have to)
2025-07-01 02:59:20.082 for j in range(blo, bhi):
2025-07-01 02:59:20.087 bj = b[j]
2025-07-01 02:59:20.091 cruncher.set_seq2(bj)
2025-07-01 02:59:20.096 for i in range(alo, ahi):
2025-07-01 02:59:20.100 ai = a[i]
2025-07-01 02:59:20.104 if ai == bj:
2025-07-01 02:59:20.109 if eqi is None:
2025-07-01 02:59:20.113 eqi, eqj = i, j
2025-07-01 02:59:20.117 continue
2025-07-01 02:59:20.122 cruncher.set_seq1(ai)
2025-07-01 02:59:20.126 # computing similarity is expensive, so use the quick
2025-07-01 02:59:20.131 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:20.135 # compares by a factor of 3.
2025-07-01 02:59:20.139 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:20.144 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:20.149 # of the computation is cached by cruncher
2025-07-01 02:59:20.153 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:20.157 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:20.162 cruncher.ratio() > best_ratio:
2025-07-01 02:59:20.166 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:20.171 if best_ratio < cutoff:
2025-07-01 02:59:20.176 # no non-identical "pretty close" pair
2025-07-01 02:59:20.180 if eqi is None:
2025-07-01 02:59:20.185 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:20.190 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:20.195 return
2025-07-01 02:59:20.200 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:20.205 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:20.211 else:
2025-07-01 02:59:20.215 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:20.222 eqi = None
2025-07-01 02:59:20.227
2025-07-01 02:59:20.238 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:20.249 # identical
2025-07-01 02:59:20.256
2025-07-01 02:59:20.263 # pump out diffs from before the synch point
2025-07-01 02:59:20.270 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:20.276
2025-07-01 02:59:20.282 # do intraline marking on the synch pair
2025-07-01 02:59:20.286 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:20.291 if eqi is None:
2025-07-01 02:59:20.296 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:20.301 atags = btags = ""
2025-07-01 02:59:20.305 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:20.309 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:20.314 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:20.318 if tag == 'replace':
2025-07-01 02:59:20.322 atags += '^' * la
2025-07-01 02:59:20.327 btags += '^' * lb
2025-07-01 02:59:20.331 elif tag == 'delete':
2025-07-01 02:59:20.335 atags += '-' * la
2025-07-01 02:59:20.340 elif tag == 'insert':
2025-07-01 02:59:20.344 btags += '+' * lb
2025-07-01 02:59:20.350 elif tag == 'equal':
2025-07-01 02:59:20.355 atags += ' ' * la
2025-07-01 02:59:20.361 btags += ' ' * lb
2025-07-01 02:59:20.366 else:
2025-07-01 02:59:20.370 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:20.375 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:20.379 else:
2025-07-01 02:59:20.384 # the synch pair is identical
2025-07-01 02:59:20.388 yield '  ' + aelt
2025-07-01 02:59:20.393
2025-07-01 02:59:20.398 # pump out diffs from after the synch point
2025-07-01 02:59:20.402 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:20.407
2025-07-01 02:59:20.411 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:20.415 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:20.419
2025-07-01 02:59:20.424 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:20.428 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:20.433 alo = 11, ahi = 1101
2025-07-01 02:59:20.437 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:20.442 blo = 11, bhi = 1101
2025-07-01 02:59:20.446
2025-07-01 02:59:20.450 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:20.455 g = []
2025-07-01 02:59:20.460 if alo < ahi:
2025-07-01 02:59:20.464 if blo < bhi:
2025-07-01 02:59:20.469 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:20.473 else:
2025-07-01 02:59:20.477 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:20.482 elif blo < bhi:
2025-07-01 02:59:20.486 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:20.491
2025-07-01 02:59:20.496 >       yield from g
2025-07-01 02:59:20.500
2025-07-01 02:59:20.504 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:20.509 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:20.513
2025-07-01 02:59:20.519 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:20.523 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:20.527 alo = 11, ahi = 1101
2025-07-01 02:59:20.532 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:20.536 blo = 11, bhi = 1101
2025-07-01 02:59:20.541
2025-07-01 02:59:20.545 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:20.549 r"""
2025-07-01 02:59:20.554 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:20.558 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:20.563 synch point, and intraline difference marking is done on the
2025-07-01 02:59:20.567 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:20.573
2025-07-01 02:59:20.577 Example:
2025-07-01 02:59:20.581
2025-07-01 02:59:20.586 >>> d = Differ()
2025-07-01 02:59:20.590 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:20.595 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:20.600 >>> print(''.join(results), end="")
2025-07-01 02:59:20.605 - abcDefghiJkl
2025-07-01 02:59:20.614 + abcdefGhijkl
2025-07-01 02:59:20.623 """
2025-07-01 02:59:20.628
2025-07-01 02:59:20.633 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:20.637 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:20.642 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:20.646 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:20.651 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:20.655
2025-07-01 02:59:20.660 # search for the pair that matches best without being identical
2025-07-01 02:59:20.665 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:20.669 # on junk -- unless we have to)
2025-07-01 02:59:20.673 for j in range(blo, bhi):
2025-07-01 02:59:20.678 bj = b[j]
2025-07-01 02:59:20.682 cruncher.set_seq2(bj)
2025-07-01 02:59:20.687 for i in range(alo, ahi):
2025-07-01 02:59:20.691 ai = a[i]
2025-07-01 02:59:20.696 if ai == bj:
2025-07-01 02:59:20.702 if eqi is None:
2025-07-01 02:59:20.706 eqi, eqj = i, j
2025-07-01 02:59:20.711 continue
2025-07-01 02:59:20.715 cruncher.set_seq1(ai)
2025-07-01 02:59:20.720 # computing similarity is expensive, so use the quick
2025-07-01 02:59:20.724 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:20.729 # compares by a factor of 3.
2025-07-01 02:59:20.733 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:20.738 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:20.742 # of the computation is cached by cruncher
2025-07-01 02:59:20.747 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:20.751 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:20.761 cruncher.ratio() > best_ratio:
2025-07-01 02:59:20.766 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:20.771 if best_ratio < cutoff:
2025-07-01 02:59:20.775 # no non-identical "pretty close" pair
2025-07-01 02:59:20.780 if eqi is None:
2025-07-01 02:59:20.784 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:20.789 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:20.795 return
2025-07-01 02:59:20.801 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:20.806 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:20.811 else:
2025-07-01 02:59:20.815 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:20.819 eqi = None
2025-07-01 02:59:20.824
2025-07-01 02:59:20.828 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:20.832 # identical
2025-07-01 02:59:20.837
2025-07-01 02:59:20.841 # pump out diffs from before the synch point
2025-07-01 02:59:20.845 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:20.849
2025-07-01 02:59:20.854 # do intraline marking on the synch pair
2025-07-01 02:59:20.858 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:20.862 if eqi is None:
2025-07-01 02:59:20.867 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:20.871 atags = btags = ""
2025-07-01 02:59:20.876 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:20.880 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:20.884 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:20.889 if tag == 'replace':
2025-07-01 02:59:20.893 atags += '^' * la
2025-07-01 02:59:20.898 btags += '^' * lb
2025-07-01 02:59:20.903 elif tag == 'delete':
2025-07-01 02:59:20.907 atags += '-' * la
2025-07-01 02:59:20.911 elif tag == 'insert':
2025-07-01 02:59:20.916 btags += '+' * lb
2025-07-01 02:59:20.920 elif tag == 'equal':
2025-07-01 02:59:20.924 atags += ' ' * la
2025-07-01 02:59:20.929 btags += ' ' * lb
2025-07-01 02:59:20.933 else:
2025-07-01 02:59:20.937 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:20.942 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:20.946 else:
2025-07-01 02:59:20.951 # the synch pair is identical
2025-07-01 02:59:20.955 yield '  ' + aelt
2025-07-01 02:59:20.959
2025-07-01 02:59:20.963 # pump out diffs from after the synch point
2025-07-01 02:59:20.968 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:20.972
2025-07-01 02:59:20.977 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:20.981 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:20.985
2025-07-01 02:59:20.990 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:20.995 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:20.999 alo = 12, ahi = 1101
2025-07-01 02:59:21.004 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:21.009 blo = 12, bhi = 1101
2025-07-01 02:59:21.013
2025-07-01 02:59:21.017 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:21.022 g = []
2025-07-01 02:59:21.026 if alo < ahi:
2025-07-01 02:59:21.031 if blo < bhi:
2025-07-01 02:59:21.035 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:21.040 else:
2025-07-01 02:59:21.044 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:21.048 elif blo < bhi:
2025-07-01 02:59:21.053 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:21.057
2025-07-01 02:59:21.062 >       yield from g
2025-07-01 02:59:21.066
2025-07-01 02:59:21.071 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:21.075 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:21.079
2025-07-01 02:59:21.084 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:21.089 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:21.093 alo = 12, ahi = 1101
2025-07-01 02:59:21.098 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:21.103 blo = 12, bhi = 1101
2025-07-01 02:59:21.107
2025-07-01 02:59:21.111 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:21.115 r"""
2025-07-01 02:59:21.120 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:21.125 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:21.129 synch point, and intraline difference marking is done on the
2025-07-01 02:59:21.133 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:21.138
2025-07-01 02:59:21.142 Example:
2025-07-01 02:59:21.147
2025-07-01 02:59:21.151 >>> d = Differ()
2025-07-01 02:59:21.156 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:21.160 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:21.165 >>> print(''.join(results), end="")
2025-07-01 02:59:21.170 - abcDefghiJkl
2025-07-01 02:59:21.179 + abcdefGhijkl
2025-07-01 02:59:21.188 """
2025-07-01 02:59:21.192
2025-07-01 02:59:21.197 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:21.202 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:21.207 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:21.211 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:21.216 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:21.221
2025-07-01 02:59:21.226 # search for the pair that matches best without being identical
2025-07-01 02:59:21.231 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:21.237 # on junk -- unless we have to)
2025-07-01 02:59:21.242 for j in range(blo, bhi):
2025-07-01 02:59:21.246 bj = b[j]
2025-07-01 02:59:21.251 cruncher.set_seq2(bj)
2025-07-01 02:59:21.256 for i in range(alo, ahi):
2025-07-01 02:59:21.260 ai = a[i]
2025-07-01 02:59:21.265 if ai == bj:
2025-07-01 02:59:21.269 if eqi is None:
2025-07-01 02:59:21.274 eqi, eqj = i, j
2025-07-01 02:59:21.278 continue
2025-07-01 02:59:21.282 cruncher.set_seq1(ai)
2025-07-01 02:59:21.287 # computing similarity is expensive, so use the quick
2025-07-01 02:59:21.291 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:21.296 # compares by a factor of 3.
2025-07-01 02:59:21.300 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:21.304 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:21.309 # of the computation is cached by cruncher
2025-07-01 02:59:21.313 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:21.318 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:21.322 cruncher.ratio() > best_ratio:
2025-07-01 02:59:21.327 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:21.332 if best_ratio < cutoff:
2025-07-01 02:59:21.337 # no non-identical "pretty close" pair
2025-07-01 02:59:21.341 if eqi is None:
2025-07-01 02:59:21.346 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:21.350 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:21.355 return
2025-07-01 02:59:21.360 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:21.364 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:21.369 else:
2025-07-01 02:59:21.374 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:21.379 eqi = None
2025-07-01 02:59:21.384
2025-07-01 02:59:21.389 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:21.393 # identical
2025-07-01 02:59:21.397
2025-07-01 02:59:21.402 # pump out diffs from before the synch point
2025-07-01 02:59:21.406 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:21.410
2025-07-01 02:59:21.415 # do intraline marking on the synch pair
2025-07-01 02:59:21.419 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:21.423 if eqi is None:
2025-07-01 02:59:21.428 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:21.432 atags = btags = ""
2025-07-01 02:59:21.436 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:21.441 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:21.445 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:21.450 if tag == 'replace':
2025-07-01 02:59:21.454 atags += '^' * la
2025-07-01 02:59:21.458 btags += '^' * lb
2025-07-01 02:59:21.463 elif tag == 'delete':
2025-07-01 02:59:21.467 atags += '-' * la
2025-07-01 02:59:21.471 elif tag == 'insert':
2025-07-01 02:59:21.475 btags += '+' * lb
2025-07-01 02:59:21.480 elif tag == 'equal':
2025-07-01 02:59:21.484 atags += ' ' * la
2025-07-01 02:59:21.489 btags += ' ' * lb
2025-07-01 02:59:21.493 else:
2025-07-01 02:59:21.497 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:21.502 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:21.507 else:
2025-07-01 02:59:21.511 # the synch pair is identical
2025-07-01 02:59:21.515 yield '  ' + aelt
2025-07-01 02:59:21.519
2025-07-01 02:59:21.524 # pump out diffs from after the synch point
2025-07-01 02:59:21.528 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:21.532
2025-07-01 02:59:21.537 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:21.541 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:21.545
2025-07-01 02:59:21.550 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:21.554 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:21.559 alo = 13, ahi = 1101
2025-07-01 02:59:21.564 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:21.568 blo = 13, bhi = 1101
2025-07-01 02:59:21.572
2025-07-01 02:59:21.577 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:21.581 g = []
2025-07-01 02:59:21.585 if alo < ahi:
2025-07-01 02:59:21.590 if blo < bhi:
2025-07-01 02:59:21.594 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:21.598 else:
2025-07-01 02:59:21.603 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:21.607 elif blo < bhi:
2025-07-01 02:59:21.612 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:21.616
2025-07-01 02:59:21.620 >       yield from g
2025-07-01 02:59:21.624
2025-07-01 02:59:21.629 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:21.633 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:21.637
2025-07-01 02:59:21.642 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:21.647 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:21.651 alo = 13, ahi = 1101
2025-07-01 02:59:21.656 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:21.661 blo = 13, bhi = 1101
2025-07-01 02:59:21.665
2025-07-01 02:59:21.670 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:21.674 r"""
2025-07-01 02:59:21.679 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:21.684 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:21.689 synch point, and intraline difference marking is done on the
2025-07-01 02:59:21.694 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:21.699
2025-07-01 02:59:21.703 Example:
2025-07-01 02:59:21.708
2025-07-01 02:59:21.712 >>> d = Differ()
2025-07-01 02:59:21.718 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:21.723 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:21.727 >>> print(''.join(results), end="")
2025-07-01 02:59:21.732 - abcDefghiJkl
2025-07-01 02:59:21.742 + abcdefGhijkl
2025-07-01 02:59:21.752 """
2025-07-01 02:59:21.757
2025-07-01 02:59:21.761 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:21.766 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:21.771 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:21.775 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:21.779 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:21.784
2025-07-01 02:59:21.788 # search for the pair that matches best without being identical
2025-07-01 02:59:21.793 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:21.797 # on junk -- unless we have to)
2025-07-01 02:59:21.801 for j in range(blo, bhi):
2025-07-01 02:59:21.806 bj = b[j]
2025-07-01 02:59:21.810 cruncher.set_seq2(bj)
2025-07-01 02:59:21.814 for i in range(alo, ahi):
2025-07-01 02:59:21.819 ai = a[i]
2025-07-01 02:59:21.823 if ai == bj:
2025-07-01 02:59:21.828 if eqi is None:
2025-07-01 02:59:21.833 eqi, eqj = i, j
2025-07-01 02:59:21.838 continue
2025-07-01 02:59:21.842 cruncher.set_seq1(ai)
2025-07-01 02:59:21.847 # computing similarity is expensive, so use the quick
2025-07-01 02:59:21.852 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:21.857 # compares by a factor of 3.
2025-07-01 02:59:21.862 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:21.866 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:21.871 # of the computation is cached by cruncher
2025-07-01 02:59:21.876 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:21.880 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:21.885 cruncher.ratio() > best_ratio:
2025-07-01 02:59:21.889 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:21.894 if best_ratio < cutoff:
2025-07-01 02:59:21.899 # no non-identical "pretty close" pair
2025-07-01 02:59:21.903 if eqi is None:
2025-07-01 02:59:21.908 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:21.912 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:21.921 return
2025-07-01 02:59:21.925 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:21.930 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:21.934 else:
2025-07-01 02:59:21.939 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:21.943 eqi = None
2025-07-01 02:59:21.947
2025-07-01 02:59:21.952 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:21.956 # identical
2025-07-01 02:59:21.961
2025-07-01 02:59:21.965 # pump out diffs from before the synch point
2025-07-01 02:59:21.970 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:21.974
2025-07-01 02:59:21.978 # do intraline marking on the synch pair
2025-07-01 02:59:21.983 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:21.987 if eqi is None:
2025-07-01 02:59:21.991 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:21.996 atags = btags = ""
2025-07-01 02:59:22.000 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:22.005 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:22.009 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:22.013 if tag == 'replace':
2025-07-01 02:59:22.018 atags += '^' * la
2025-07-01 02:59:22.022 btags += '^' * lb
2025-07-01 02:59:22.026 elif tag == 'delete':
2025-07-01 02:59:22.031 atags += '-' * la
2025-07-01 02:59:22.035 elif tag == 'insert':
2025-07-01 02:59:22.039 btags += '+' * lb
2025-07-01 02:59:22.044 elif tag == 'equal':
2025-07-01 02:59:22.048 atags += ' ' * la
2025-07-01 02:59:22.053 btags += ' ' * lb
2025-07-01 02:59:22.057 else:
2025-07-01 02:59:22.062 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:22.066 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:22.071 else:
2025-07-01 02:59:22.075 # the synch pair is identical
2025-07-01 02:59:22.080 yield '  ' + aelt
2025-07-01 02:59:22.084
2025-07-01 02:59:22.089 # pump out diffs from after the synch point
2025-07-01 02:59:22.093 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:22.098
2025-07-01 02:59:22.103 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:22.107 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:22.112
2025-07-01 02:59:22.118 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:22.123 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:22.127 alo = 14, ahi = 1101
2025-07-01 02:59:22.133 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:22.137 blo = 14, bhi = 1101
2025-07-01 02:59:22.141
2025-07-01 02:59:22.146 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:22.150 g = []
2025-07-01 02:59:22.154 if alo < ahi:
2025-07-01 02:59:22.159 if blo < bhi:
2025-07-01 02:59:22.163 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:22.168 else:
2025-07-01 02:59:22.172 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:22.176 elif blo < bhi:
2025-07-01 02:59:22.181 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:22.185
2025-07-01 02:59:22.189 >       yield from g
2025-07-01 02:59:22.194
2025-07-01 02:59:22.198 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:22.202 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:22.207
2025-07-01 02:59:22.211 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:22.216 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:22.220 alo = 14, ahi = 1101
2025-07-01 02:59:22.226 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:22.230 blo = 14, bhi = 1101
2025-07-01 02:59:22.234
2025-07-01 02:59:22.239 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:22.243 r"""
2025-07-01 02:59:22.248 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:22.252 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:22.257 synch point, and intraline difference marking is done on the
2025-07-01 02:59:22.262 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:22.267
2025-07-01 02:59:22.271 Example:
2025-07-01 02:59:22.276
2025-07-01 02:59:22.281 >>> d = Differ()
2025-07-01 02:59:22.285 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:22.290 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:22.294 >>> print(''.join(results), end="")
2025-07-01 02:59:22.300 - abcDefghiJkl
2025-07-01 02:59:22.310 + abcdefGhijkl
2025-07-01 02:59:22.319 """
2025-07-01 02:59:22.323
2025-07-01 02:59:22.328 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:22.333 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:22.338 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:22.344 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:22.350 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:22.356
2025-07-01 02:59:22.362 # search for the pair that matches best without being identical
2025-07-01 02:59:22.367 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:22.372 # on junk -- unless we have to)
2025-07-01 02:59:22.377 for j in range(blo, bhi):
2025-07-01 02:59:22.382 bj = b[j]
2025-07-01 02:59:22.387 cruncher.set_seq2(bj)
2025-07-01 02:59:22.392 for i in range(alo, ahi):
2025-07-01 02:59:22.397 ai = a[i]
2025-07-01 02:59:22.402 if ai == bj:
2025-07-01 02:59:22.407 if eqi is None:
2025-07-01 02:59:22.411 eqi, eqj = i, j
2025-07-01 02:59:22.416 continue
2025-07-01 02:59:22.422 cruncher.set_seq1(ai)
2025-07-01 02:59:22.426 # computing similarity is expensive, so use the quick
2025-07-01 02:59:22.431 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:22.436 # compares by a factor of 3.
2025-07-01 02:59:22.440 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:22.445 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:22.449 # of the computation is cached by cruncher
2025-07-01 02:59:22.454 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:22.459 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:22.463 cruncher.ratio() > best_ratio:
2025-07-01 02:59:22.468 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:22.472 if best_ratio < cutoff:
2025-07-01 02:59:22.477 # no non-identical "pretty close" pair
2025-07-01 02:59:22.481 if eqi is None:
2025-07-01 02:59:22.486 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:22.490 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:22.495 return
2025-07-01 02:59:22.500 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:22.505 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:22.510 else:
2025-07-01 02:59:22.515 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:22.519 eqi = None
2025-07-01 02:59:22.523
2025-07-01 02:59:22.528 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:22.533 # identical
2025-07-01 02:59:22.538
2025-07-01 02:59:22.542 # pump out diffs from before the synch point
2025-07-01 02:59:22.547 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:22.551
2025-07-01 02:59:22.556 # do intraline marking on the synch pair
2025-07-01 02:59:22.560 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:22.565 if eqi is None:
2025-07-01 02:59:22.569 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:22.574 atags = btags = ""
2025-07-01 02:59:22.578 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:22.583 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:22.587 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:22.592 if tag == 'replace':
2025-07-01 02:59:22.596 atags += '^' * la
2025-07-01 02:59:22.601 btags += '^' * lb
2025-07-01 02:59:22.605 elif tag == 'delete':
2025-07-01 02:59:22.610 atags += '-' * la
2025-07-01 02:59:22.615 elif tag == 'insert':
2025-07-01 02:59:22.620 btags += '+' * lb
2025-07-01 02:59:22.624 elif tag == 'equal':
2025-07-01 02:59:22.629 atags += ' ' * la
2025-07-01 02:59:22.633 btags += ' ' * lb
2025-07-01 02:59:22.638 else:
2025-07-01 02:59:22.642 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:22.647 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:22.651 else:
2025-07-01 02:59:22.656 # the synch pair is identical
2025-07-01 02:59:22.661 yield '  ' + aelt
2025-07-01 02:59:22.665
2025-07-01 02:59:22.670 # pump out diffs from after the synch point
2025-07-01 02:59:22.675 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:22.679
2025-07-01 02:59:22.684 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:22.689 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:22.693
2025-07-01 02:59:22.698 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:22.704 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:22.708 alo = 15, ahi = 1101
2025-07-01 02:59:22.713 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:22.718 blo = 15, bhi = 1101
2025-07-01 02:59:22.722
2025-07-01 02:59:22.726 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:22.731 g = []
2025-07-01 02:59:22.736 if alo < ahi:
2025-07-01 02:59:22.740 if blo < bhi:
2025-07-01 02:59:22.746 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:22.750 else:
2025-07-01 02:59:22.755 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:22.760 elif blo < bhi:
2025-07-01 02:59:22.765 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:22.769
2025-07-01 02:59:22.774 >       yield from g
2025-07-01 02:59:22.780
2025-07-01 02:59:22.786 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:22.792 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:22.798
2025-07-01 02:59:22.805 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:22.812 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:22.818 alo = 15, ahi = 1101
2025-07-01 02:59:22.824 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:22.830 blo = 15, bhi = 1101
2025-07-01 02:59:22.836
2025-07-01 02:59:22.842 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:22.848 r"""
2025-07-01 02:59:22.852 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:22.857 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:22.862 synch point, and intraline difference marking is done on the
2025-07-01 02:59:22.868 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:22.872
2025-07-01 02:59:22.877 Example:
2025-07-01 02:59:22.882
2025-07-01 02:59:22.886 >>> d = Differ()
2025-07-01 02:59:22.891 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:22.896 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:22.900 >>> print(''.join(results), end="")
2025-07-01 02:59:22.905 - abcDefghiJkl
2025-07-01 02:59:22.914 + abcdefGhijkl
2025-07-01 02:59:22.923 """
2025-07-01 02:59:22.927
2025-07-01 02:59:22.932 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:22.936 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:22.941 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:22.946 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:22.950 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:22.955
2025-07-01 02:59:22.959 # search for the pair that matches best without being identical
2025-07-01 02:59:22.964 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:22.969 # on junk -- unless we have to)
2025-07-01 02:59:22.973 for j in range(blo, bhi):
2025-07-01 02:59:22.978 bj = b[j]
2025-07-01 02:59:22.982 cruncher.set_seq2(bj)
2025-07-01 02:59:22.987 for i in range(alo, ahi):
2025-07-01 02:59:22.991 ai = a[i]
2025-07-01 02:59:22.996 if ai == bj:
2025-07-01 02:59:23.000 if eqi is None:
2025-07-01 02:59:23.005 eqi, eqj = i, j
2025-07-01 02:59:23.009 continue
2025-07-01 02:59:23.014 cruncher.set_seq1(ai)
2025-07-01 02:59:23.018 # computing similarity is expensive, so use the quick
2025-07-01 02:59:23.023 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:23.027 # compares by a factor of 3.
2025-07-01 02:59:23.032 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:23.037 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:23.043 # of the computation is cached by cruncher
2025-07-01 02:59:23.047 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:23.052 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:23.057 cruncher.ratio() > best_ratio:
2025-07-01 02:59:23.061 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:23.066 if best_ratio < cutoff:
2025-07-01 02:59:23.070 # no non-identical "pretty close" pair
2025-07-01 02:59:23.075 if eqi is None:
2025-07-01 02:59:23.080 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:23.085 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:23.089 return
2025-07-01 02:59:23.094 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:23.098 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:23.103 else:
2025-07-01 02:59:23.107 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:23.112 eqi = None
2025-07-01 02:59:23.116
2025-07-01 02:59:23.122 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:23.127 # identical
2025-07-01 02:59:23.133
2025-07-01 02:59:23.141 # pump out diffs from before the synch point
2025-07-01 02:59:23.148 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:23.155
2025-07-01 02:59:23.162 # do intraline marking on the synch pair
2025-07-01 02:59:23.169 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:23.175 if eqi is None:
2025-07-01 02:59:23.181 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:23.187 atags = btags = ""
2025-07-01 02:59:23.193 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:23.200 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:23.206 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:23.212 if tag == 'replace':
2025-07-01 02:59:23.218 atags += '^' * la
2025-07-01 02:59:23.224 btags += '^' * lb
2025-07-01 02:59:23.230 elif tag == 'delete':
2025-07-01 02:59:23.235 atags += '-' * la
2025-07-01 02:59:23.243 elif tag == 'insert':
2025-07-01 02:59:23.250 btags += '+' * lb
2025-07-01 02:59:23.255 elif tag == 'equal':
2025-07-01 02:59:23.261 atags += ' ' * la
2025-07-01 02:59:23.267 btags += ' ' * lb
2025-07-01 02:59:23.272 else:
2025-07-01 02:59:23.278 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:23.284 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:23.290 else:
2025-07-01 02:59:23.296 # the synch pair is identical
2025-07-01 02:59:23.302 yield '  ' + aelt
2025-07-01 02:59:23.307
2025-07-01 02:59:23.313 # pump out diffs from after the synch point
2025-07-01 02:59:23.319 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:23.325
2025-07-01 02:59:23.331 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:23.337 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:23.343
2025-07-01 02:59:23.348 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:23.355 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:23.361 alo = 16, ahi = 1101
2025-07-01 02:59:23.368 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:23.373 blo = 16, bhi = 1101
2025-07-01 02:59:23.379
2025-07-01 02:59:23.385 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:23.391 g = []
2025-07-01 02:59:23.396 if alo < ahi:
2025-07-01 02:59:23.401 if blo < bhi:
2025-07-01 02:59:23.407 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:23.414 else:
2025-07-01 02:59:23.418 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:23.422 elif blo < bhi:
2025-07-01 02:59:23.427 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:23.432
2025-07-01 02:59:23.437 >       yield from g
2025-07-01 02:59:23.441
2025-07-01 02:59:23.446 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:23.450 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:23.455
2025-07-01 02:59:23.459 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:23.464 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:23.468 alo = 16, ahi = 1101
2025-07-01 02:59:23.473 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:23.478 blo = 16, bhi = 1101
2025-07-01 02:59:23.482
2025-07-01 02:59:23.486 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:23.490 r"""
2025-07-01 02:59:23.495 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:23.500 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:23.504 synch point, and intraline difference marking is done on the
2025-07-01 02:59:23.509 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:23.513
2025-07-01 02:59:23.517 Example:
2025-07-01 02:59:23.522
2025-07-01 02:59:23.526 >>> d = Differ()
2025-07-01 02:59:23.530 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:23.535 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:23.539 >>> print(''.join(results), end="")
2025-07-01 02:59:23.543 - abcDefghiJkl
2025-07-01 02:59:23.552 + abcdefGhijkl
2025-07-01 02:59:23.560 """
2025-07-01 02:59:23.565
2025-07-01 02:59:23.569 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:23.573 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:23.578 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:23.582 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:23.587 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:23.591
2025-07-01 02:59:23.596 # search for the pair that matches best without being identical
2025-07-01 02:59:23.600 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:23.610 # on junk -- unless we have to)
2025-07-01 02:59:23.618 for j in range(blo, bhi):
2025-07-01 02:59:23.625 bj = b[j]
2025-07-01 02:59:23.630 cruncher.set_seq2(bj)
2025-07-01 02:59:23.634 for i in range(alo, ahi):
2025-07-01 02:59:23.638 ai = a[i]
2025-07-01 02:59:23.643 if ai == bj:
2025-07-01 02:59:23.647 if eqi is None:
2025-07-01 02:59:23.651 eqi, eqj = i, j
2025-07-01 02:59:23.656 continue
2025-07-01 02:59:23.660 cruncher.set_seq1(ai)
2025-07-01 02:59:23.665 # computing similarity is expensive, so use the quick
2025-07-01 02:59:23.669 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:23.674 # compares by a factor of 3.
2025-07-01 02:59:23.678 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:23.683 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:23.687 # of the computation is cached by cruncher
2025-07-01 02:59:23.692 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:23.697 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:23.701 cruncher.ratio() > best_ratio:
2025-07-01 02:59:23.706 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:23.710 if best_ratio < cutoff:
2025-07-01 02:59:23.715 # no non-identical "pretty close" pair
2025-07-01 02:59:23.719 if eqi is None:
2025-07-01 02:59:23.724 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:23.729 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:23.733 return
2025-07-01 02:59:23.738 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:23.742 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:23.746 else:
2025-07-01 02:59:23.750 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:23.755 eqi = None
2025-07-01 02:59:23.759
2025-07-01 02:59:23.764 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:23.770 # identical
2025-07-01 02:59:23.774
2025-07-01 02:59:23.779 # pump out diffs from before the synch point
2025-07-01 02:59:23.783 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:23.787
2025-07-01 02:59:23.792 # do intraline marking on the synch pair
2025-07-01 02:59:23.796 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:23.800 if eqi is None:
2025-07-01 02:59:23.805 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:23.809 atags = btags = ""
2025-07-01 02:59:23.814 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:23.818 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:23.822 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:23.827 if tag == 'replace':
2025-07-01 02:59:23.831 atags += '^' * la
2025-07-01 02:59:23.835 btags += '^' * lb
2025-07-01 02:59:23.840 elif tag == 'delete':
2025-07-01 02:59:23.844 atags += '-' * la
2025-07-01 02:59:23.849 elif tag == 'insert':
2025-07-01 02:59:23.853 btags += '+' * lb
2025-07-01 02:59:23.857 elif tag == 'equal':
2025-07-01 02:59:23.862 atags += ' ' * la
2025-07-01 02:59:23.866 btags += ' ' * lb
2025-07-01 02:59:23.870 else:
2025-07-01 02:59:23.875 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:23.879 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:23.884 else:
2025-07-01 02:59:23.888 # the synch pair is identical
2025-07-01 02:59:23.892 yield '  ' + aelt
2025-07-01 02:59:23.897
2025-07-01 02:59:23.901 # pump out diffs from after the synch point
2025-07-01 02:59:23.906 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:23.910
2025-07-01 02:59:23.914 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:23.919 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:23.923
2025-07-01 02:59:23.928 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:23.933 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:23.937 alo = 17, ahi = 1101
2025-07-01 02:59:23.942 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:23.946 blo = 17, bhi = 1101
2025-07-01 02:59:23.950
2025-07-01 02:59:23.955 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:23.959 g = []
2025-07-01 02:59:23.963 if alo < ahi:
2025-07-01 02:59:23.967 if blo < bhi:
2025-07-01 02:59:23.972 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:23.976 else:
2025-07-01 02:59:23.980 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:23.986 elif blo < bhi:
2025-07-01 02:59:23.990 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:23.994
2025-07-01 02:59:24.000 >       yield from g
2025-07-01 02:59:24.005
2025-07-01 02:59:24.009 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:24.018 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:24.023
2025-07-01 02:59:24.027 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:24.032 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:24.037 alo = 17, ahi = 1101
2025-07-01 02:59:24.041 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:24.046 blo = 17, bhi = 1101
2025-07-01 02:59:24.050
2025-07-01 02:59:24.054 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:24.058 r"""
2025-07-01 02:59:24.063 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:24.068 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:24.072 synch point, and intraline difference marking is done on the
2025-07-01 02:59:24.077 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:24.081
2025-07-01 02:59:24.086 Example:
2025-07-01 02:59:24.090
2025-07-01 02:59:24.094 >>> d = Differ()
2025-07-01 02:59:24.099 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:24.104 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:24.108 >>> print(''.join(results), end="")
2025-07-01 02:59:24.113 - abcDefghiJkl
2025-07-01 02:59:24.122 + abcdefGhijkl
2025-07-01 02:59:24.130 """
2025-07-01 02:59:24.135
2025-07-01 02:59:24.139 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:24.144 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:24.148 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:24.153 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:24.157 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:24.162
2025-07-01 02:59:24.166 # search for the pair that matches best without being identical
2025-07-01 02:59:24.171 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:24.176 # on junk -- unless we have to)
2025-07-01 02:59:24.180 for j in range(blo, bhi):
2025-07-01 02:59:24.184 bj = b[j]
2025-07-01 02:59:24.189 cruncher.set_seq2(bj)
2025-07-01 02:59:24.193 for i in range(alo, ahi):
2025-07-01 02:59:24.198 ai = a[i]
2025-07-01 02:59:24.202 if ai == bj:
2025-07-01 02:59:24.206 if eqi is None:
2025-07-01 02:59:24.211 eqi, eqj = i, j
2025-07-01 02:59:24.215 continue
2025-07-01 02:59:24.219 cruncher.set_seq1(ai)
2025-07-01 02:59:24.224 # computing similarity is expensive, so use the quick
2025-07-01 02:59:24.229 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:24.233 # compares by a factor of 3.
2025-07-01 02:59:24.238 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:24.243 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:24.247 # of the computation is cached by cruncher
2025-07-01 02:59:24.252 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:24.256 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:24.261 cruncher.ratio() > best_ratio:
2025-07-01 02:59:24.265 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:24.270 if best_ratio < cutoff:
2025-07-01 02:59:24.275 # no non-identical "pretty close" pair
2025-07-01 02:59:24.279 if eqi is None:
2025-07-01 02:59:24.284 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:24.289 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:24.293 return
2025-07-01 02:59:24.298 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:24.302 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:24.307 else:
2025-07-01 02:59:24.311 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:24.316 eqi = None
2025-07-01 02:59:24.322
2025-07-01 02:59:24.327 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:24.331 # identical
2025-07-01 02:59:24.335
2025-07-01 02:59:24.340 # pump out diffs from before the synch point
2025-07-01 02:59:24.345 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:24.349
2025-07-01 02:59:24.354 # do intraline marking on the synch pair
2025-07-01 02:59:24.358 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:24.362 if eqi is None:
2025-07-01 02:59:24.367 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:24.372 atags = btags = ""
2025-07-01 02:59:24.377 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:24.382 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:24.387 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:24.391 if tag == 'replace':
2025-07-01 02:59:24.396 atags += '^' * la
2025-07-01 02:59:24.400 btags += '^' * lb
2025-07-01 02:59:24.405 elif tag == 'delete':
2025-07-01 02:59:24.409 atags += '-' * la
2025-07-01 02:59:24.413 elif tag == 'insert':
2025-07-01 02:59:24.418 btags += '+' * lb
2025-07-01 02:59:24.422 elif tag == 'equal':
2025-07-01 02:59:24.427 atags += ' ' * la
2025-07-01 02:59:24.432 btags += ' ' * lb
2025-07-01 02:59:24.436 else:
2025-07-01 02:59:24.441 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:24.445 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:24.450 else:
2025-07-01 02:59:24.455 # the synch pair is identical
2025-07-01 02:59:24.459 yield '  ' + aelt
2025-07-01 02:59:24.463
2025-07-01 02:59:24.479 # pump out diffs from after the synch point
2025-07-01 02:59:24.490 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:24.495
2025-07-01 02:59:24.500 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:24.505 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:24.513
2025-07-01 02:59:24.517 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:24.523 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:24.527 alo = 18, ahi = 1101
2025-07-01 02:59:24.533 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:24.537 blo = 18, bhi = 1101
2025-07-01 02:59:24.541
2025-07-01 02:59:24.546 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:24.550 g = []
2025-07-01 02:59:24.554 if alo < ahi:
2025-07-01 02:59:24.559 if blo < bhi:
2025-07-01 02:59:24.563 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:24.568 else:
2025-07-01 02:59:24.573 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:24.578 elif blo < bhi:
2025-07-01 02:59:24.582 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:24.587
2025-07-01 02:59:24.591 >       yield from g
2025-07-01 02:59:24.596
2025-07-01 02:59:24.601 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:24.606 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:24.611
2025-07-01 02:59:24.615 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:24.620 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:24.626 alo = 18, ahi = 1101
2025-07-01 02:59:24.630 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:24.634 blo = 18, bhi = 1101
2025-07-01 02:59:24.639
2025-07-01 02:59:24.643 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:24.648 r"""
2025-07-01 02:59:24.652 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:24.657 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:24.662 synch point, and intraline difference marking is done on the
2025-07-01 02:59:24.666 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:24.670
2025-07-01 02:59:24.675 Example:
2025-07-01 02:59:24.680
2025-07-01 02:59:24.684 >>> d = Differ()
2025-07-01 02:59:24.688 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:24.693 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:24.698 >>> print(''.join(results), end="")
2025-07-01 02:59:24.702 - abcDefghiJkl
2025-07-01 02:59:24.711 + abcdefGhijkl
2025-07-01 02:59:24.719 """
2025-07-01 02:59:24.723
2025-07-01 02:59:24.728 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:24.732 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:24.737 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:24.741 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:24.746 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:24.750
2025-07-01 02:59:24.754 # search for the pair that matches best without being identical
2025-07-01 02:59:24.760 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:24.765 # on junk -- unless we have to)
2025-07-01 02:59:24.769 for j in range(blo, bhi):
2025-07-01 02:59:24.774 bj = b[j]
2025-07-01 02:59:24.779 cruncher.set_seq2(bj)
2025-07-01 02:59:24.783 for i in range(alo, ahi):
2025-07-01 02:59:24.788 ai = a[i]
2025-07-01 02:59:24.793 if ai == bj:
2025-07-01 02:59:24.797 if eqi is None:
2025-07-01 02:59:24.802 eqi, eqj = i, j
2025-07-01 02:59:24.807 continue
2025-07-01 02:59:24.811 cruncher.set_seq1(ai)
2025-07-01 02:59:24.816 # computing similarity is expensive, so use the quick
2025-07-01 02:59:24.820 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:24.825 # compares by a factor of 3.
2025-07-01 02:59:24.829 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:24.834 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:24.838 # of the computation is cached by cruncher
2025-07-01 02:59:24.842 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:24.847 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:24.852 cruncher.ratio() > best_ratio:
2025-07-01 02:59:24.856 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:24.861 if best_ratio < cutoff:
2025-07-01 02:59:24.865 # no non-identical "pretty close" pair
2025-07-01 02:59:24.869 if eqi is None:
2025-07-01 02:59:24.874 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:24.878 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:24.883 return
2025-07-01 02:59:24.887 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:24.892 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:24.896 else:
2025-07-01 02:59:24.901 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:24.905 eqi = None
2025-07-01 02:59:24.910
2025-07-01 02:59:24.914 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:24.919 # identical
2025-07-01 02:59:24.923
2025-07-01 02:59:24.928 # pump out diffs from before the synch point
2025-07-01 02:59:24.932 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:24.937
2025-07-01 02:59:24.942 # do intraline marking on the synch pair
2025-07-01 02:59:24.947 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:24.951 if eqi is None:
2025-07-01 02:59:24.956 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:24.960 atags = btags = ""
2025-07-01 02:59:24.964 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:24.970 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:24.974 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:24.980 if tag == 'replace':
2025-07-01 02:59:24.984 atags += '^' * la
2025-07-01 02:59:24.988 btags += '^' * lb
2025-07-01 02:59:24.993 elif tag == 'delete':
2025-07-01 02:59:24.997 atags += '-' * la
2025-07-01 02:59:25.002 elif tag == 'insert':
2025-07-01 02:59:25.006 btags += '+' * lb
2025-07-01 02:59:25.011 elif tag == 'equal':
2025-07-01 02:59:25.015 atags += ' ' * la
2025-07-01 02:59:25.020 btags += ' ' * lb
2025-07-01 02:59:25.024 else:
2025-07-01 02:59:25.029 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:25.033 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:25.038 else:
2025-07-01 02:59:25.042 # the synch pair is identical
2025-07-01 02:59:25.048 yield '  ' + aelt
2025-07-01 02:59:25.053
2025-07-01 02:59:25.058 # pump out diffs from after the synch point
2025-07-01 02:59:25.063 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:25.067
2025-07-01 02:59:25.071 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:25.076 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:25.081
2025-07-01 02:59:25.085 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:25.090 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:25.095 alo = 19, ahi = 1101
2025-07-01 02:59:25.100 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:25.104 blo = 19, bhi = 1101
2025-07-01 02:59:25.108
2025-07-01 02:59:25.113 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:25.117 g = []
2025-07-01 02:59:25.121 if alo < ahi:
2025-07-01 02:59:25.126 if blo < bhi:
2025-07-01 02:59:25.130 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:25.135 else:
2025-07-01 02:59:25.140 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:25.145 elif blo < bhi:
2025-07-01 02:59:25.150 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:25.154
2025-07-01 02:59:25.158 >       yield from g
2025-07-01 02:59:25.163
2025-07-01 02:59:25.170 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:25.175 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:25.179
2025-07-01 02:59:25.184 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:25.189 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:25.193 alo = 19, ahi = 1101
2025-07-01 02:59:25.198 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:25.202 blo = 19, bhi = 1101
2025-07-01 02:59:25.207
2025-07-01 02:59:25.211 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:25.216 r"""
2025-07-01 02:59:25.221 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:25.225 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:25.230 synch point, and intraline difference marking is done on the
2025-07-01 02:59:25.234 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:25.239
2025-07-01 02:59:25.243 Example:
2025-07-01 02:59:25.247
2025-07-01 02:59:25.251 >>> d = Differ()
2025-07-01 02:59:25.256 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:25.260 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:25.265 >>> print(''.join(results), end="")
2025-07-01 02:59:25.269 - abcDefghiJkl
2025-07-01 02:59:25.278 + abcdefGhijkl
2025-07-01 02:59:25.287 """
2025-07-01 02:59:25.292
2025-07-01 02:59:25.296 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:25.301 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:25.305 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:25.310 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:25.315 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:25.320
2025-07-01 02:59:25.324 # search for the pair that matches best without being identical
2025-07-01 02:59:25.329 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:25.333 # on junk -- unless we have to)
2025-07-01 02:59:25.338 for j in range(blo, bhi):
2025-07-01 02:59:25.343 bj = b[j]
2025-07-01 02:59:25.347 cruncher.set_seq2(bj)
2025-07-01 02:59:25.354 for i in range(alo, ahi):
2025-07-01 02:59:25.359 ai = a[i]
2025-07-01 02:59:25.364 if ai == bj:
2025-07-01 02:59:25.369 if eqi is None:
2025-07-01 02:59:25.373 eqi, eqj = i, j
2025-07-01 02:59:25.378 continue
2025-07-01 02:59:25.382 cruncher.set_seq1(ai)
2025-07-01 02:59:25.389 # computing similarity is expensive, so use the quick
2025-07-01 02:59:25.396 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:25.401 # compares by a factor of 3.
2025-07-01 02:59:25.406 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:25.411 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:25.415 # of the computation is cached by cruncher
2025-07-01 02:59:25.420 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:25.425 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:25.429 cruncher.ratio() > best_ratio:
2025-07-01 02:59:25.434 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:25.439 if best_ratio < cutoff:
2025-07-01 02:59:25.444 # no non-identical "pretty close" pair
2025-07-01 02:59:25.448 if eqi is None:
2025-07-01 02:59:25.452 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:25.457 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:25.462 return
2025-07-01 02:59:25.466 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:25.471 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:25.475 else:
2025-07-01 02:59:25.480 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:25.484 eqi = None
2025-07-01 02:59:25.489
2025-07-01 02:59:25.493 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:25.498 # identical
2025-07-01 02:59:25.504
2025-07-01 02:59:25.508 # pump out diffs from before the synch point
2025-07-01 02:59:25.513 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:25.518
2025-07-01 02:59:25.522 # do intraline marking on the synch pair
2025-07-01 02:59:25.528 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:25.532 if eqi is None:
2025-07-01 02:59:25.537 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:25.541 atags = btags = ""
2025-07-01 02:59:25.546 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:25.550 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:25.555 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:25.559 if tag == 'replace':
2025-07-01 02:59:25.564 atags += '^' * la
2025-07-01 02:59:25.569 btags += '^' * lb
2025-07-01 02:59:25.573 elif tag == 'delete':
2025-07-01 02:59:25.578 atags += '-' * la
2025-07-01 02:59:25.582 elif tag == 'insert':
2025-07-01 02:59:25.586 btags += '+' * lb
2025-07-01 02:59:25.591 elif tag == 'equal':
2025-07-01 02:59:25.595 atags += ' ' * la
2025-07-01 02:59:25.599 btags += ' ' * lb
2025-07-01 02:59:25.605 else:
2025-07-01 02:59:25.611 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:25.616 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:25.620 else:
2025-07-01 02:59:25.625 # the synch pair is identical
2025-07-01 02:59:25.629 yield '  ' + aelt
2025-07-01 02:59:25.634
2025-07-01 02:59:25.638 # pump out diffs from after the synch point
2025-07-01 02:59:25.643 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:25.647
2025-07-01 02:59:25.651 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:25.656 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:25.660
2025-07-01 02:59:25.665 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:25.670 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:25.674 alo = 20, ahi = 1101
2025-07-01 02:59:25.679 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:25.684 blo = 20, bhi = 1101
2025-07-01 02:59:25.688
2025-07-01 02:59:25.692 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:25.697 g = []
2025-07-01 02:59:25.701 if alo < ahi:
2025-07-01 02:59:25.706 if blo < bhi:
2025-07-01 02:59:25.710 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:25.715 else:
2025-07-01 02:59:25.721 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:25.727 elif blo < bhi:
2025-07-01 02:59:25.733 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:25.737
2025-07-01 02:59:25.742 >       yield from g
2025-07-01 02:59:25.746
2025-07-01 02:59:25.750 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:25.755 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:25.759
2025-07-01 02:59:25.764 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:25.769 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:25.773 alo = 20, ahi = 1101
2025-07-01 02:59:25.778 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:25.782 blo = 20, bhi = 1101
2025-07-01 02:59:25.787
2025-07-01 02:59:25.793 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:25.797 r"""
2025-07-01 02:59:25.803 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:25.807 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:25.812 synch point, and intraline difference marking is done on the
2025-07-01 02:59:25.817 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:25.821
2025-07-01 02:59:25.826 Example:
2025-07-01 02:59:25.831
2025-07-01 02:59:25.836 >>> d = Differ()
2025-07-01 02:59:25.841 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:25.846 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:25.850 >>> print(''.join(results), end="")
2025-07-01 02:59:25.855 - abcDefghiJkl
2025-07-01 02:59:25.863 + abcdefGhijkl
2025-07-01 02:59:25.872 """
2025-07-01 02:59:25.876
2025-07-01 02:59:25.881 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:25.885 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:25.890 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:25.894 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:25.899 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:25.903
2025-07-01 02:59:25.908 # search for the pair that matches best without being identical
2025-07-01 02:59:25.912 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:25.916 # on junk -- unless we have to)
2025-07-01 02:59:25.922 for j in range(blo, bhi):
2025-07-01 02:59:25.928 bj = b[j]
2025-07-01 02:59:25.933 cruncher.set_seq2(bj)
2025-07-01 02:59:25.937 for i in range(alo, ahi):
2025-07-01 02:59:25.941 ai = a[i]
2025-07-01 02:59:25.946 if ai == bj:
2025-07-01 02:59:25.951 if eqi is None:
2025-07-01 02:59:25.956 eqi, eqj = i, j
2025-07-01 02:59:25.963 continue
2025-07-01 02:59:25.969 cruncher.set_seq1(ai)
2025-07-01 02:59:25.975 # computing similarity is expensive, so use the quick
2025-07-01 02:59:25.981 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:25.988 # compares by a factor of 3.
2025-07-01 02:59:25.995 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:26.001 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:26.007 # of the computation is cached by cruncher
2025-07-01 02:59:26.012 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:26.018 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:26.022 cruncher.ratio() > best_ratio:
2025-07-01 02:59:26.027 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:26.031 if best_ratio < cutoff:
2025-07-01 02:59:26.036 # no non-identical "pretty close" pair
2025-07-01 02:59:26.040 if eqi is None:
2025-07-01 02:59:26.045 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:26.049 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:26.054 return
2025-07-01 02:59:26.058 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:26.063 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:26.067 else:
2025-07-01 02:59:26.072 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:26.076 eqi = None
2025-07-01 02:59:26.081
2025-07-01 02:59:26.085 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:26.090 # identical
2025-07-01 02:59:26.094
2025-07-01 02:59:26.098 # pump out diffs from before the synch point
2025-07-01 02:59:26.103 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:26.107
2025-07-01 02:59:26.112 # do intraline marking on the synch pair
2025-07-01 02:59:26.116 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:26.121 if eqi is None:
2025-07-01 02:59:26.125 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:26.129 atags = btags = ""
2025-07-01 02:59:26.134 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:26.138 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:26.143 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:26.148 if tag == 'replace':
2025-07-01 02:59:26.153 atags += '^' * la
2025-07-01 02:59:26.157 btags += '^' * lb
2025-07-01 02:59:26.162 elif tag == 'delete':
2025-07-01 02:59:26.167 atags += '-' * la
2025-07-01 02:59:26.171 elif tag == 'insert':
2025-07-01 02:59:26.176 btags += '+' * lb
2025-07-01 02:59:26.180 elif tag == 'equal':
2025-07-01 02:59:26.185 atags += ' ' * la
2025-07-01 02:59:26.189 btags += ' ' * lb
2025-07-01 02:59:26.194 else:
2025-07-01 02:59:26.199 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:26.203 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:26.207 else:
2025-07-01 02:59:26.212 # the synch pair is identical
2025-07-01 02:59:26.216 yield '  ' + aelt
2025-07-01 02:59:26.221
2025-07-01 02:59:26.225 # pump out diffs from after the synch point
2025-07-01 02:59:26.230 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:26.234
2025-07-01 02:59:26.238 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:26.243 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:26.248
2025-07-01 02:59:26.252 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:26.257 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:26.261 alo = 21, ahi = 1101
2025-07-01 02:59:26.266 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:26.270 blo = 21, bhi = 1101
2025-07-01 02:59:26.275
2025-07-01 02:59:26.279 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:26.283 g = []
2025-07-01 02:59:26.288 if alo < ahi:
2025-07-01 02:59:26.293 if blo < bhi:
2025-07-01 02:59:26.297 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:26.302 else:
2025-07-01 02:59:26.307 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:26.311 elif blo < bhi:
2025-07-01 02:59:26.316 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:26.320
2025-07-01 02:59:26.325 >       yield from g
2025-07-01 02:59:26.330
2025-07-01 02:59:26.335 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:26.341 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:26.345
2025-07-01 02:59:26.350 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:26.356 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:26.360 alo = 21, ahi = 1101
2025-07-01 02:59:26.366 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:26.370 blo = 21, bhi = 1101
2025-07-01 02:59:26.375
2025-07-01 02:59:26.381 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:26.386 r"""
2025-07-01 02:59:26.390 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:26.395 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:26.399 synch point, and intraline difference marking is done on the
2025-07-01 02:59:26.405 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:26.410
2025-07-01 02:59:26.414 Example:
2025-07-01 02:59:26.418
2025-07-01 02:59:26.423 >>> d = Differ()
2025-07-01 02:59:26.428 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:26.432 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:26.437 >>> print(''.join(results), end="")
2025-07-01 02:59:26.442 - abcDefghiJkl
2025-07-01 02:59:26.451 + abcdefGhijkl
2025-07-01 02:59:26.459 """
2025-07-01 02:59:26.464
2025-07-01 02:59:26.469 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:26.473 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:26.478 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:26.482 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:26.487 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:26.491
2025-07-01 02:59:26.495 # search for the pair that matches best without being identical
2025-07-01 02:59:26.500 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:26.504 # on junk -- unless we have to)
2025-07-01 02:59:26.508 for j in range(blo, bhi):
2025-07-01 02:59:26.513 bj = b[j]
2025-07-01 02:59:26.517 cruncher.set_seq2(bj)
2025-07-01 02:59:26.522 for i in range(alo, ahi):
2025-07-01 02:59:26.526 ai = a[i]
2025-07-01 02:59:26.530 if ai == bj:
2025-07-01 02:59:26.535 if eqi is None:
2025-07-01 02:59:26.539 eqi, eqj = i, j
2025-07-01 02:59:26.544 continue
2025-07-01 02:59:26.548 cruncher.set_seq1(ai)
2025-07-01 02:59:26.553 # computing similarity is expensive, so use the quick
2025-07-01 02:59:26.557 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:26.562 # compares by a factor of 3.
2025-07-01 02:59:26.566 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:26.571 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:26.575 # of the computation is cached by cruncher
2025-07-01 02:59:26.580 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:26.584 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:26.589 cruncher.ratio() > best_ratio:
2025-07-01 02:59:26.593 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:26.597 if best_ratio < cutoff:
2025-07-01 02:59:26.602 # no non-identical "pretty close" pair
2025-07-01 02:59:26.606 if eqi is None:
2025-07-01 02:59:26.611 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:26.616 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:26.620 return
2025-07-01 02:59:26.625 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:26.631 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:26.637 else:
2025-07-01 02:59:26.643 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:26.649 eqi = None
2025-07-01 02:59:26.654
2025-07-01 02:59:26.660 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:26.666 # identical
2025-07-01 02:59:26.671
2025-07-01 02:59:26.675 # pump out diffs from before the synch point
2025-07-01 02:59:26.680 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:26.684
2025-07-01 02:59:26.689 # do intraline marking on the synch pair
2025-07-01 02:59:26.693 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:26.697 if eqi is None:
2025-07-01 02:59:26.702 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:26.707 atags = btags = ""
2025-07-01 02:59:26.711 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:26.715 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:26.720 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:26.724 if tag == 'replace':
2025-07-01 02:59:26.728 atags += '^' * la
2025-07-01 02:59:26.733 btags += '^' * lb
2025-07-01 02:59:26.737 elif tag == 'delete':
2025-07-01 02:59:26.741 atags += '-' * la
2025-07-01 02:59:26.746 elif tag == 'insert':
2025-07-01 02:59:26.750 btags += '+' * lb
2025-07-01 02:59:26.754 elif tag == 'equal':
2025-07-01 02:59:26.759 atags += ' ' * la
2025-07-01 02:59:26.763 btags += ' ' * lb
2025-07-01 02:59:26.767 else:
2025-07-01 02:59:26.772 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:26.776 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:26.780 else:
2025-07-01 02:59:26.785 # the synch pair is identical
2025-07-01 02:59:26.789 yield '  ' + aelt
2025-07-01 02:59:26.793
2025-07-01 02:59:26.798 # pump out diffs from after the synch point
2025-07-01 02:59:26.802 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:26.807
2025-07-01 02:59:26.811 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:26.816 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:26.821
2025-07-01 02:59:26.827 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:26.832 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:26.837 alo = 22, ahi = 1101
2025-07-01 02:59:26.842 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:26.846 blo = 22, bhi = 1101
2025-07-01 02:59:26.850
2025-07-01 02:59:26.855 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:26.859 g = []
2025-07-01 02:59:26.863 if alo < ahi:
2025-07-01 02:59:26.867 if blo < bhi:
2025-07-01 02:59:26.872 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:26.876 else:
2025-07-01 02:59:26.881 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:26.886 elif blo < bhi:
2025-07-01 02:59:26.891 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:26.896
2025-07-01 02:59:26.900 >       yield from g
2025-07-01 02:59:26.905
2025-07-01 02:59:26.909 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:26.914 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:26.918
2025-07-01 02:59:26.923 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:26.927 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:26.932 alo = 22, ahi = 1101
2025-07-01 02:59:26.936 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:26.941 blo = 22, bhi = 1101
2025-07-01 02:59:26.945
2025-07-01 02:59:26.949 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:26.953 r"""
2025-07-01 02:59:26.958 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:26.962 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:26.967 synch point, and intraline difference marking is done on the
2025-07-01 02:59:26.972 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:26.976
2025-07-01 02:59:26.981 Example:
2025-07-01 02:59:26.985
2025-07-01 02:59:26.990 >>> d = Differ()
2025-07-01 02:59:26.995 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:27.000 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:27.006 >>> print(''.join(results), end="")
2025-07-01 02:59:27.011 - abcDefghiJkl
2025-07-01 02:59:27.022 + abcdefGhijkl
2025-07-01 02:59:27.031 """
2025-07-01 02:59:27.035
2025-07-01 02:59:27.040 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:27.044 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:27.049 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:27.053 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:27.057 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:27.062
2025-07-01 02:59:27.068 # search for the pair that matches best without being identical
2025-07-01 02:59:27.072 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:27.077 # on junk -- unless we have to)
2025-07-01 02:59:27.081 for j in range(blo, bhi):
2025-07-01 02:59:27.086 bj = b[j]
2025-07-01 02:59:27.090 cruncher.set_seq2(bj)
2025-07-01 02:59:27.094 for i in range(alo, ahi):
2025-07-01 02:59:27.099 ai = a[i]
2025-07-01 02:59:27.103 if ai == bj:
2025-07-01 02:59:27.108 if eqi is None:
2025-07-01 02:59:27.112 eqi, eqj = i, j
2025-07-01 02:59:27.116 continue
2025-07-01 02:59:27.121 cruncher.set_seq1(ai)
2025-07-01 02:59:27.127 # computing similarity is expensive, so use the quick
2025-07-01 02:59:27.132 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:27.138 # compares by a factor of 3.
2025-07-01 02:59:27.143 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:27.148 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:27.154 # of the computation is cached by cruncher
2025-07-01 02:59:27.159 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:27.164 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:27.170 cruncher.ratio() > best_ratio:
2025-07-01 02:59:27.175 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:27.180 if best_ratio < cutoff:
2025-07-01 02:59:27.184 # no non-identical "pretty close" pair
2025-07-01 02:59:27.190 if eqi is None:
2025-07-01 02:59:27.195 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:27.200 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:27.205 return
2025-07-01 02:59:27.211 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:27.216 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:27.221 else:
2025-07-01 02:59:27.227 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:27.232 eqi = None
2025-07-01 02:59:27.237
2025-07-01 02:59:27.242 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:27.247 # identical
2025-07-01 02:59:27.253
2025-07-01 02:59:27.258 # pump out diffs from before the synch point
2025-07-01 02:59:27.263 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:27.268
2025-07-01 02:59:27.273 # do intraline marking on the synch pair
2025-07-01 02:59:27.279 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:27.284 if eqi is None:
2025-07-01 02:59:27.289 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:27.295 atags = btags = ""
2025-07-01 02:59:27.300 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:27.305 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:27.311 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:27.316 if tag == 'replace':
2025-07-01 02:59:27.321 atags += '^' * la
2025-07-01 02:59:27.326 btags += '^' * lb
2025-07-01 02:59:27.332 elif tag == 'delete':
2025-07-01 02:59:27.337 atags += '-' * la
2025-07-01 02:59:27.341 elif tag == 'insert':
2025-07-01 02:59:27.346 btags += '+' * lb
2025-07-01 02:59:27.350 elif tag == 'equal':
2025-07-01 02:59:27.355 atags += ' ' * la
2025-07-01 02:59:27.359 btags += ' ' * lb
2025-07-01 02:59:27.363 else:
2025-07-01 02:59:27.368 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:27.372 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:27.377 else:
2025-07-01 02:59:27.382 # the synch pair is identical
2025-07-01 02:59:27.386 yield '  ' + aelt
2025-07-01 02:59:27.392
2025-07-01 02:59:27.397 # pump out diffs from after the synch point
2025-07-01 02:59:27.402 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:27.408
2025-07-01 02:59:27.412 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:27.417 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:27.421
2025-07-01 02:59:27.425 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:27.430 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:27.434 alo = 23, ahi = 1101
2025-07-01 02:59:27.439 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:27.443 blo = 23, bhi = 1101
2025-07-01 02:59:27.448
2025-07-01 02:59:27.452 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:27.457 g = []
2025-07-01 02:59:27.461 if alo < ahi:
2025-07-01 02:59:27.466 if blo < bhi:
2025-07-01 02:59:27.471 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:27.475 else:
2025-07-01 02:59:27.480 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:27.484 elif blo < bhi:
2025-07-01 02:59:27.489 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:27.493
2025-07-01 02:59:27.497 >       yield from g
2025-07-01 02:59:27.502
2025-07-01 02:59:27.506 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:27.511 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:27.515
2025-07-01 02:59:27.520 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:27.525 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:27.529 alo = 23, ahi = 1101
2025-07-01 02:59:27.534 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:27.539 blo = 23, bhi = 1101
2025-07-01 02:59:27.543
2025-07-01 02:59:27.548 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:27.552 r"""
2025-07-01 02:59:27.557 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:27.562 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:27.566 synch point, and intraline difference marking is done on the
2025-07-01 02:59:27.570 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:27.574
2025-07-01 02:59:27.579 Example:
2025-07-01 02:59:27.583
2025-07-01 02:59:27.587 >>> d = Differ()
2025-07-01 02:59:27.592 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:27.597 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:27.601 >>> print(''.join(results), end="")
2025-07-01 02:59:27.606 - abcDefghiJkl
2025-07-01 02:59:27.615 + abcdefGhijkl
2025-07-01 02:59:27.624 """
2025-07-01 02:59:27.629
2025-07-01 02:59:27.633 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:27.638 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:27.642 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:27.646 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:27.651 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:27.655
2025-07-01 02:59:27.660 # search for the pair that matches best without being identical
2025-07-01 02:59:27.664 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:27.669 # on junk -- unless we have to)
2025-07-01 02:59:27.673 for j in range(blo, bhi):
2025-07-01 02:59:27.678 bj = b[j]
2025-07-01 02:59:27.684 cruncher.set_seq2(bj)
2025-07-01 02:59:27.690 for i in range(alo, ahi):
2025-07-01 02:59:27.694 ai = a[i]
2025-07-01 02:59:27.698 if ai == bj:
2025-07-01 02:59:27.703 if eqi is None:
2025-07-01 02:59:27.707 eqi, eqj = i, j
2025-07-01 02:59:27.712 continue
2025-07-01 02:59:27.716 cruncher.set_seq1(ai)
2025-07-01 02:59:27.721 # computing similarity is expensive, so use the quick
2025-07-01 02:59:27.725 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:27.730 # compares by a factor of 3.
2025-07-01 02:59:27.734 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:27.739 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:27.743 # of the computation is cached by cruncher
2025-07-01 02:59:27.748 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:27.752 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:27.757 cruncher.ratio() > best_ratio:
2025-07-01 02:59:27.761 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:27.766 if best_ratio < cutoff:
2025-07-01 02:59:27.770 # no non-identical "pretty close" pair
2025-07-01 02:59:27.775 if eqi is None:
2025-07-01 02:59:27.779 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:27.784 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:27.788 return
2025-07-01 02:59:27.792 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:27.797 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:27.801 else:
2025-07-01 02:59:27.805 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:27.810 eqi = None
2025-07-01 02:59:27.814
2025-07-01 02:59:27.818 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:27.822 # identical
2025-07-01 02:59:27.827
2025-07-01 02:59:27.831 # pump out diffs from before the synch point
2025-07-01 02:59:27.836 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:27.841
2025-07-01 02:59:27.847 # do intraline marking on the synch pair
2025-07-01 02:59:27.852 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:27.858 if eqi is None:
2025-07-01 02:59:27.863 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:27.867 atags = btags = ""
2025-07-01 02:59:27.871 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:27.876 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:27.881 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:27.885 if tag == 'replace':
2025-07-01 02:59:27.889 atags += '^' * la
2025-07-01 02:59:27.893 btags += '^' * lb
2025-07-01 02:59:27.898 elif tag == 'delete':
2025-07-01 02:59:27.903 atags += '-' * la
2025-07-01 02:59:27.907 elif tag == 'insert':
2025-07-01 02:59:27.911 btags += '+' * lb
2025-07-01 02:59:27.915 elif tag == 'equal':
2025-07-01 02:59:27.920 atags += ' ' * la
2025-07-01 02:59:27.924 btags += ' ' * lb
2025-07-01 02:59:27.928 else:
2025-07-01 02:59:27.933 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:27.937 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:27.941 else:
2025-07-01 02:59:27.946 # the synch pair is identical
2025-07-01 02:59:27.950 yield '  ' + aelt
2025-07-01 02:59:27.955
2025-07-01 02:59:27.959 # pump out diffs from after the synch point
2025-07-01 02:59:27.964 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:27.968
2025-07-01 02:59:27.973 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:27.978 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:27.982
2025-07-01 02:59:27.987 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:27.992 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:27.997 alo = 26, ahi = 1101
2025-07-01 02:59:28.002 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:28.008 blo = 26, bhi = 1101
2025-07-01 02:59:28.015
2025-07-01 02:59:28.021 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:28.027 g = []
2025-07-01 02:59:28.033 if alo < ahi:
2025-07-01 02:59:28.039 if blo < bhi:
2025-07-01 02:59:28.045 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:28.051 else:
2025-07-01 02:59:28.057 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:28.063 elif blo < bhi:
2025-07-01 02:59:28.069 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:28.074
2025-07-01 02:59:28.080 >       yield from g
2025-07-01 02:59:28.085
2025-07-01 02:59:28.091 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:28.095 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:28.100
2025-07-01 02:59:28.106 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:28.112 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:28.117 alo = 26, ahi = 1101
2025-07-01 02:59:28.123 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:28.128 blo = 26, bhi = 1101
2025-07-01 02:59:28.132
2025-07-01 02:59:28.137 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:28.141 r"""
2025-07-01 02:59:28.146 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:28.150 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:28.155 synch point, and intraline difference marking is done on the
2025-07-01 02:59:28.159 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:28.163
2025-07-01 02:59:28.167 Example:
2025-07-01 02:59:28.172
2025-07-01 02:59:28.176 >>> d = Differ()
2025-07-01 02:59:28.181 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:28.185 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:28.190 >>> print(''.join(results), end="")
2025-07-01 02:59:28.195 - abcDefghiJkl
2025-07-01 02:59:28.205 + abcdefGhijkl
2025-07-01 02:59:28.214 """
2025-07-01 02:59:28.219
2025-07-01 02:59:28.224 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:28.230 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:28.236 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:28.242 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:28.247 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:28.252
2025-07-01 02:59:28.257 # search for the pair that matches best without being identical
2025-07-01 02:59:28.262 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:28.268 # on junk -- unless we have to)
2025-07-01 02:59:28.273 for j in range(blo, bhi):
2025-07-01 02:59:28.278 bj = b[j]
2025-07-01 02:59:28.283 cruncher.set_seq2(bj)
2025-07-01 02:59:28.288 for i in range(alo, ahi):
2025-07-01 02:59:28.293 ai = a[i]
2025-07-01 02:59:28.299 if ai == bj:
2025-07-01 02:59:28.305 if eqi is None:
2025-07-01 02:59:28.311 eqi, eqj = i, j
2025-07-01 02:59:28.317 continue
2025-07-01 02:59:28.323 cruncher.set_seq1(ai)
2025-07-01 02:59:28.328 # computing similarity is expensive, so use the quick
2025-07-01 02:59:28.333 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:28.337 # compares by a factor of 3.
2025-07-01 02:59:28.342 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:28.347 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:28.351 # of the computation is cached by cruncher
2025-07-01 02:59:28.355 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:28.360 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:28.366 cruncher.ratio() > best_ratio:
2025-07-01 02:59:28.371 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:28.375 if best_ratio < cutoff:
2025-07-01 02:59:28.380 # no non-identical "pretty close" pair
2025-07-01 02:59:28.385 if eqi is None:
2025-07-01 02:59:28.390 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:28.395 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:28.399 return
2025-07-01 02:59:28.404 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:28.409 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:28.414 else:
2025-07-01 02:59:28.419 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:28.425 eqi = None
2025-07-01 02:59:28.429
2025-07-01 02:59:28.434 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:28.439 # identical
2025-07-01 02:59:28.443
2025-07-01 02:59:28.448 # pump out diffs from before the synch point
2025-07-01 02:59:28.453 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:28.457
2025-07-01 02:59:28.462 # do intraline marking on the synch pair
2025-07-01 02:59:28.466 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:28.470 if eqi is None:
2025-07-01 02:59:28.475 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:28.479 atags = btags = ""
2025-07-01 02:59:28.484 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:28.489 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:28.493 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:28.498 if tag == 'replace':
2025-07-01 02:59:28.502 atags += '^' * la
2025-07-01 02:59:28.506 btags += '^' * lb
2025-07-01 02:59:28.511 elif tag == 'delete':
2025-07-01 02:59:28.515 atags += '-' * la
2025-07-01 02:59:28.520 elif tag == 'insert':
2025-07-01 02:59:28.525 btags += '+' * lb
2025-07-01 02:59:28.529 elif tag == 'equal':
2025-07-01 02:59:28.533 atags += ' ' * la
2025-07-01 02:59:28.538 btags += ' ' * lb
2025-07-01 02:59:28.542 else:
2025-07-01 02:59:28.547 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:28.552 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:28.556 else:
2025-07-01 02:59:28.561 # the synch pair is identical
2025-07-01 02:59:28.566 yield '  ' + aelt
2025-07-01 02:59:28.571
2025-07-01 02:59:28.575 # pump out diffs from after the synch point
2025-07-01 02:59:28.580 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:28.585
2025-07-01 02:59:28.589 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:28.594 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:28.599
2025-07-01 02:59:28.603 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:28.609 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:28.613 alo = 27, ahi = 1101
2025-07-01 02:59:28.618 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:28.623 blo = 27, bhi = 1101
2025-07-01 02:59:28.627
2025-07-01 02:59:28.632 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:28.636 g = []
2025-07-01 02:59:28.641 if alo < ahi:
2025-07-01 02:59:28.645 if blo < bhi:
2025-07-01 02:59:28.650 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:28.654 else:
2025-07-01 02:59:28.658 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:28.663 elif blo < bhi:
2025-07-01 02:59:28.667 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:28.671
2025-07-01 02:59:28.676 >       yield from g
2025-07-01 02:59:28.680
2025-07-01 02:59:28.685 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:28.689 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:28.693
2025-07-01 02:59:28.698 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:28.702 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:28.707 alo = 27, ahi = 1101
2025-07-01 02:59:28.712 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:28.716 blo = 27, bhi = 1101
2025-07-01 02:59:28.720
2025-07-01 02:59:28.725 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:28.730 r"""
2025-07-01 02:59:28.735 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:28.741 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:28.746 synch point, and intraline difference marking is done on the
2025-07-01 02:59:28.750 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:28.755
2025-07-01 02:59:28.759 Example:
2025-07-01 02:59:28.764
2025-07-01 02:59:28.769 >>> d = Differ()
2025-07-01 02:59:28.773 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:28.777 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:28.782 >>> print(''.join(results), end="")
2025-07-01 02:59:28.786 - abcDefghiJkl
2025-07-01 02:59:28.795 + abcdefGhijkl
2025-07-01 02:59:28.804 """
2025-07-01 02:59:28.808
2025-07-01 02:59:28.813 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:28.817 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:28.822 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:28.826 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:28.831 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:28.836
2025-07-01 02:59:28.842 # search for the pair that matches best without being identical
2025-07-01 02:59:28.849 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:28.855 # on junk -- unless we have to)
2025-07-01 02:59:28.861 for j in range(blo, bhi):
2025-07-01 02:59:28.867 bj = b[j]
2025-07-01 02:59:28.873 cruncher.set_seq2(bj)
2025-07-01 02:59:28.879 for i in range(alo, ahi):
2025-07-01 02:59:28.885 ai = a[i]
2025-07-01 02:59:28.890 if ai == bj:
2025-07-01 02:59:28.894 if eqi is None:
2025-07-01 02:59:28.898 eqi, eqj = i, j
2025-07-01 02:59:28.903 continue
2025-07-01 02:59:28.907 cruncher.set_seq1(ai)
2025-07-01 02:59:28.911 # computing similarity is expensive, so use the quick
2025-07-01 02:59:28.916 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:28.920 # compares by a factor of 3.
2025-07-01 02:59:28.925 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:28.929 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:28.934 # of the computation is cached by cruncher
2025-07-01 02:59:28.938 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:28.942 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:28.947 cruncher.ratio() > best_ratio:
2025-07-01 02:59:28.951 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:28.956 if best_ratio < cutoff:
2025-07-01 02:59:28.960 # no non-identical "pretty close" pair
2025-07-01 02:59:28.965 if eqi is None:
2025-07-01 02:59:28.969 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:28.973 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:28.978 return
2025-07-01 02:59:28.982 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:28.987 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:28.991 else:
2025-07-01 02:59:28.996 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:29.000 eqi = None
2025-07-01 02:59:29.005
2025-07-01 02:59:29.010 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:29.014 # identical
2025-07-01 02:59:29.018
2025-07-01 02:59:29.023 # pump out diffs from before the synch point
2025-07-01 02:59:29.027 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:29.033
2025-07-01 02:59:29.038 # do intraline marking on the synch pair
2025-07-01 02:59:29.042 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:29.047 if eqi is None:
2025-07-01 02:59:29.053 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:29.059 atags = btags = ""
2025-07-01 02:59:29.065 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:29.070 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:29.076 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:29.082 if tag == 'replace':
2025-07-01 02:59:29.088 atags += '^' * la
2025-07-01 02:59:29.094 btags += '^' * lb
2025-07-01 02:59:29.099 elif tag == 'delete':
2025-07-01 02:59:29.105 atags += '-' * la
2025-07-01 02:59:29.111 elif tag == 'insert':
2025-07-01 02:59:29.117 btags += '+' * lb
2025-07-01 02:59:29.122 elif tag == 'equal':
2025-07-01 02:59:29.128 atags += ' ' * la
2025-07-01 02:59:29.134 btags += ' ' * lb
2025-07-01 02:59:29.140 else:
2025-07-01 02:59:29.146 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:29.151 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:29.155 else:
2025-07-01 02:59:29.160 # the synch pair is identical
2025-07-01 02:59:29.165 yield '  ' + aelt
2025-07-01 02:59:29.169
2025-07-01 02:59:29.173 # pump out diffs from after the synch point
2025-07-01 02:59:29.178 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:29.183
2025-07-01 02:59:29.187 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:29.191 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:29.196
2025-07-01 02:59:29.200 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:29.205 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:29.209 alo = 28, ahi = 1101
2025-07-01 02:59:29.215 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:29.219 blo = 28, bhi = 1101
2025-07-01 02:59:29.223
2025-07-01 02:59:29.228 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:29.233 g = []
2025-07-01 02:59:29.238 if alo < ahi:
2025-07-01 02:59:29.242 if blo < bhi:
2025-07-01 02:59:29.246 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:29.251 else:
2025-07-01 02:59:29.255 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:29.260 elif blo < bhi:
2025-07-01 02:59:29.265 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:29.269
2025-07-01 02:59:29.273 >       yield from g
2025-07-01 02:59:29.278
2025-07-01 02:59:29.282 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:29.287 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:29.291
2025-07-01 02:59:29.296 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:29.301 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:29.305 alo = 28, ahi = 1101
2025-07-01 02:59:29.310 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:29.315 blo = 28, bhi = 1101
2025-07-01 02:59:29.319
2025-07-01 02:59:29.323 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:29.328 r"""
2025-07-01 02:59:29.332 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:29.337 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:29.341 synch point, and intraline difference marking is done on the
2025-07-01 02:59:29.346 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:29.351
2025-07-01 02:59:29.355 Example:
2025-07-01 02:59:29.360
2025-07-01 02:59:29.364 >>> d = Differ()
2025-07-01 02:59:29.369 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:29.374 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:29.378 >>> print(''.join(results), end="")
2025-07-01 02:59:29.383 - abcDefghiJkl
2025-07-01 02:59:29.391 + abcdefGhijkl
2025-07-01 02:59:29.400 """
2025-07-01 02:59:29.405
2025-07-01 02:59:29.409 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:29.414 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:29.418 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:29.423 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:29.427 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:29.431
2025-07-01 02:59:29.436 # search for the pair that matches best without being identical
2025-07-01 02:59:29.440 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:29.445 # on junk -- unless we have to)
2025-07-01 02:59:29.449 for j in range(blo, bhi):
2025-07-01 02:59:29.453 bj = b[j]
2025-07-01 02:59:29.458 cruncher.set_seq2(bj)
2025-07-01 02:59:29.463 for i in range(alo, ahi):
2025-07-01 02:59:29.468 ai = a[i]
2025-07-01 02:59:29.473 if ai == bj:
2025-07-01 02:59:29.477 if eqi is None:
2025-07-01 02:59:29.482 eqi, eqj = i, j
2025-07-01 02:59:29.486 continue
2025-07-01 02:59:29.491 cruncher.set_seq1(ai)
2025-07-01 02:59:29.495 # computing similarity is expensive, so use the quick
2025-07-01 02:59:29.500 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:29.504 # compares by a factor of 3.
2025-07-01 02:59:29.509 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:29.514 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:29.518 # of the computation is cached by cruncher
2025-07-01 02:59:29.523 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:29.529 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:29.533 cruncher.ratio() > best_ratio:
2025-07-01 02:59:29.538 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:29.543 if best_ratio < cutoff:
2025-07-01 02:59:29.547 # no non-identical "pretty close" pair
2025-07-01 02:59:29.552 if eqi is None:
2025-07-01 02:59:29.556 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:29.561 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:29.566 return
2025-07-01 02:59:29.571 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:29.576 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:29.580 else:
2025-07-01 02:59:29.584 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:29.589 eqi = None
2025-07-01 02:59:29.593
2025-07-01 02:59:29.598 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:29.602 # identical
2025-07-01 02:59:29.607
2025-07-01 02:59:29.611 # pump out diffs from before the synch point
2025-07-01 02:59:29.616 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:29.621
2025-07-01 02:59:29.625 # do intraline marking on the synch pair
2025-07-01 02:59:29.630 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:29.634 if eqi is None:
2025-07-01 02:59:29.639 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:29.643 atags = btags = ""
2025-07-01 02:59:29.647 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:29.652 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:29.657 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:29.662 if tag == 'replace':
2025-07-01 02:59:29.666 atags += '^' * la
2025-07-01 02:59:29.670 btags += '^' * lb
2025-07-01 02:59:29.675 elif tag == 'delete':
2025-07-01 02:59:29.679 atags += '-' * la
2025-07-01 02:59:29.684 elif tag == 'insert':
2025-07-01 02:59:29.688 btags += '+' * lb
2025-07-01 02:59:29.692 elif tag == 'equal':
2025-07-01 02:59:29.697 atags += ' ' * la
2025-07-01 02:59:29.701 btags += ' ' * lb
2025-07-01 02:59:29.706 else:
2025-07-01 02:59:29.710 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:29.714 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:29.719 else:
2025-07-01 02:59:29.723 # the synch pair is identical
2025-07-01 02:59:29.728 yield '  ' + aelt
2025-07-01 02:59:29.732
2025-07-01 02:59:29.737 # pump out diffs from after the synch point
2025-07-01 02:59:29.742 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:29.747
2025-07-01 02:59:29.751 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:29.757 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:29.762
2025-07-01 02:59:29.767 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:29.772 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:29.776 alo = 29, ahi = 1101
2025-07-01 02:59:29.781 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:29.785 blo = 29, bhi = 1101
2025-07-01 02:59:29.789
2025-07-01 02:59:29.794 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:29.798 g = []
2025-07-01 02:59:29.802 if alo < ahi:
2025-07-01 02:59:29.807 if blo < bhi:
2025-07-01 02:59:29.811 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:29.815 else:
2025-07-01 02:59:29.819 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:29.824 elif blo < bhi:
2025-07-01 02:59:29.828 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:29.833
2025-07-01 02:59:29.837 >       yield from g
2025-07-01 02:59:29.841
2025-07-01 02:59:29.846 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:29.850 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:29.855
2025-07-01 02:59:29.859 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:29.865 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:29.869 alo = 29, ahi = 1101
2025-07-01 02:59:29.874 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:29.878 blo = 29, bhi = 1101
2025-07-01 02:59:29.883
2025-07-01 02:59:29.887 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:29.891 r"""
2025-07-01 02:59:29.896 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:29.900 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:29.904 synch point, and intraline difference marking is done on the
2025-07-01 02:59:29.909 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:29.913
2025-07-01 02:59:29.917 Example:
2025-07-01 02:59:29.921
2025-07-01 02:59:29.926 >>> d = Differ()
2025-07-01 02:59:29.930 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:29.934 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:29.939 >>> print(''.join(results), end="")
2025-07-01 02:59:29.943 - abcDefghiJkl
2025-07-01 02:59:29.951 + abcdefGhijkl
2025-07-01 02:59:29.960 """
2025-07-01 02:59:29.964
2025-07-01 02:59:29.969 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:29.973 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:29.977 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:29.982 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:29.986 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:29.991
2025-07-01 02:59:29.995 # search for the pair that matches best without being identical
2025-07-01 02:59:29.999 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:30.004 # on junk -- unless we have to)
2025-07-01 02:59:30.010 for j in range(blo, bhi):
2025-07-01 02:59:30.015 bj = b[j]
2025-07-01 02:59:30.020 cruncher.set_seq2(bj)
2025-07-01 02:59:30.024 for i in range(alo, ahi):
2025-07-01 02:59:30.029 ai = a[i]
2025-07-01 02:59:30.033 if ai == bj:
2025-07-01 02:59:30.037 if eqi is None:
2025-07-01 02:59:30.042 eqi, eqj = i, j
2025-07-01 02:59:30.046 continue
2025-07-01 02:59:30.050 cruncher.set_seq1(ai)
2025-07-01 02:59:30.055 # computing similarity is expensive, so use the quick
2025-07-01 02:59:30.059 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:30.064 # compares by a factor of 3.
2025-07-01 02:59:30.068 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:30.073 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:30.077 # of the computation is cached by cruncher
2025-07-01 02:59:30.081 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:30.086 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:30.090 cruncher.ratio() > best_ratio:
2025-07-01 02:59:30.095 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:30.099 if best_ratio < cutoff:
2025-07-01 02:59:30.103 # no non-identical "pretty close" pair
2025-07-01 02:59:30.108 if eqi is None:
2025-07-01 02:59:30.113 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:30.117 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:30.121 return
2025-07-01 02:59:30.126 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:30.131 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:30.135 else:
2025-07-01 02:59:30.140 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:30.144 eqi = None
2025-07-01 02:59:30.148
2025-07-01 02:59:30.152 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:30.157 # identical
2025-07-01 02:59:30.161
2025-07-01 02:59:30.165 # pump out diffs from before the synch point
2025-07-01 02:59:30.170 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:30.174
2025-07-01 02:59:30.179 # do intraline marking on the synch pair
2025-07-01 02:59:30.183 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:30.187 if eqi is None:
2025-07-01 02:59:30.192 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:30.197 atags = btags = ""
2025-07-01 02:59:30.201 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:30.205 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:30.209 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:30.214 if tag == 'replace':
2025-07-01 02:59:30.218 atags += '^' * la
2025-07-01 02:59:30.222 btags += '^' * lb
2025-07-01 02:59:30.227 elif tag == 'delete':
2025-07-01 02:59:30.231 atags += '-' * la
2025-07-01 02:59:30.235 elif tag == 'insert':
2025-07-01 02:59:30.240 btags += '+' * lb
2025-07-01 02:59:30.244 elif tag == 'equal':
2025-07-01 02:59:30.248 atags += ' ' * la
2025-07-01 02:59:30.253 btags += ' ' * lb
2025-07-01 02:59:30.257 else:
2025-07-01 02:59:30.261 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:30.266 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:30.270 else:
2025-07-01 02:59:30.274 # the synch pair is identical
2025-07-01 02:59:30.278 yield '  ' + aelt
2025-07-01 02:59:30.283
2025-07-01 02:59:30.287 # pump out diffs from after the synch point
2025-07-01 02:59:30.292 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:30.296
2025-07-01 02:59:30.300 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:30.305 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:30.309
2025-07-01 02:59:30.313 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:30.318 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:30.322 alo = 30, ahi = 1101
2025-07-01 02:59:30.327 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:30.331 blo = 30, bhi = 1101
2025-07-01 02:59:30.335
2025-07-01 02:59:30.340 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:30.344 g = []
2025-07-01 02:59:30.349 if alo < ahi:
2025-07-01 02:59:30.353 if blo < bhi:
2025-07-01 02:59:30.357 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:30.363 else:
2025-07-01 02:59:30.368 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:30.372 elif blo < bhi:
2025-07-01 02:59:30.377 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:30.381
2025-07-01 02:59:30.385 >       yield from g
2025-07-01 02:59:30.390
2025-07-01 02:59:30.394 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:30.399 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:30.403
2025-07-01 02:59:30.408 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:30.413 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:30.417 alo = 30, ahi = 1101
2025-07-01 02:59:30.422 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:30.426 blo = 30, bhi = 1101
2025-07-01 02:59:30.431
2025-07-01 02:59:30.435 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:30.439 r"""
2025-07-01 02:59:30.444 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:30.448 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:30.453 synch point, and intraline difference marking is done on the
2025-07-01 02:59:30.457 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:30.462
2025-07-01 02:59:30.466 Example:
2025-07-01 02:59:30.471
2025-07-01 02:59:30.475 >>> d = Differ()
2025-07-01 02:59:30.480 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:30.484 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:30.489 >>> print(''.join(results), end="")
2025-07-01 02:59:30.494 - abcDefghiJkl
2025-07-01 02:59:30.503 + abcdefGhijkl
2025-07-01 02:59:30.512 """
2025-07-01 02:59:30.517
2025-07-01 02:59:30.521 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:30.525 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:30.530 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:30.534 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:30.539 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:30.543
2025-07-01 02:59:30.547 # search for the pair that matches best without being identical
2025-07-01 02:59:30.552 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:30.556 # on junk -- unless we have to)
2025-07-01 02:59:30.561 for j in range(blo, bhi):
2025-07-01 02:59:30.565 bj = b[j]
2025-07-01 02:59:30.570 cruncher.set_seq2(bj)
2025-07-01 02:59:30.574 for i in range(alo, ahi):
2025-07-01 02:59:30.578 ai = a[i]
2025-07-01 02:59:30.583 if ai == bj:
2025-07-01 02:59:30.587 if eqi is None:
2025-07-01 02:59:30.592 eqi, eqj = i, j
2025-07-01 02:59:30.596 continue
2025-07-01 02:59:30.601 cruncher.set_seq1(ai)
2025-07-01 02:59:30.605 # computing similarity is expensive, so use the quick
2025-07-01 02:59:30.609 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:30.614 # compares by a factor of 3.
2025-07-01 02:59:30.618 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:30.623 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:30.628 # of the computation is cached by cruncher
2025-07-01 02:59:30.632 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:30.637 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:30.643 cruncher.ratio() > best_ratio:
2025-07-01 02:59:30.648 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:30.653 if best_ratio < cutoff:
2025-07-01 02:59:30.657 # no non-identical "pretty close" pair
2025-07-01 02:59:30.661 if eqi is None:
2025-07-01 02:59:30.666 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:30.670 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:30.675 return
2025-07-01 02:59:30.679 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:30.684 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:30.688 else:
2025-07-01 02:59:30.692 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:30.697 eqi = None
2025-07-01 02:59:30.701
2025-07-01 02:59:30.706 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:30.710 # identical
2025-07-01 02:59:30.715
2025-07-01 02:59:30.719 # pump out diffs from before the synch point
2025-07-01 02:59:30.724 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:30.728
2025-07-01 02:59:30.733 # do intraline marking on the synch pair
2025-07-01 02:59:30.737 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:30.741 if eqi is None:
2025-07-01 02:59:30.746 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:30.750 atags = btags = ""
2025-07-01 02:59:30.755 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:30.759 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:30.764 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:30.769 if tag == 'replace':
2025-07-01 02:59:30.774 atags += '^' * la
2025-07-01 02:59:30.778 btags += '^' * lb
2025-07-01 02:59:30.783 elif tag == 'delete':
2025-07-01 02:59:30.787 atags += '-' * la
2025-07-01 02:59:30.791 elif tag == 'insert':
2025-07-01 02:59:30.796 btags += '+' * lb
2025-07-01 02:59:30.800 elif tag == 'equal':
2025-07-01 02:59:30.805 atags += ' ' * la
2025-07-01 02:59:30.809 btags += ' ' * lb
2025-07-01 02:59:30.813 else:
2025-07-01 02:59:30.818 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:30.823 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:30.827 else:
2025-07-01 02:59:30.832 # the synch pair is identical
2025-07-01 02:59:30.836 yield '  ' + aelt
2025-07-01 02:59:30.840
2025-07-01 02:59:30.845 # pump out diffs from after the synch point
2025-07-01 02:59:30.849 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:30.854
2025-07-01 02:59:30.858 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:30.863 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:30.868
2025-07-01 02:59:30.872 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:30.877 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:30.883 alo = 31, ahi = 1101
2025-07-01 02:59:30.888 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:30.893 blo = 31, bhi = 1101
2025-07-01 02:59:30.897
2025-07-01 02:59:30.901 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:30.906 g = []
2025-07-01 02:59:30.910 if alo < ahi:
2025-07-01 02:59:30.915 if blo < bhi:
2025-07-01 02:59:30.921 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:30.925 else:
2025-07-01 02:59:30.929 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:30.934 elif blo < bhi:
2025-07-01 02:59:30.938 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:30.942
2025-07-01 02:59:30.946 >       yield from g
2025-07-01 02:59:30.951
2025-07-01 02:59:30.955 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:30.960 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:30.964
2025-07-01 02:59:30.969 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:30.973 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:30.978 alo = 31, ahi = 1101
2025-07-01 02:59:30.983 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:30.988 blo = 31, bhi = 1101
2025-07-01 02:59:30.992
2025-07-01 02:59:30.996 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:31.001 r"""
2025-07-01 02:59:31.005 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:31.009 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:31.014 synch point, and intraline difference marking is done on the
2025-07-01 02:59:31.018 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:31.023
2025-07-01 02:59:31.027 Example:
2025-07-01 02:59:31.032
2025-07-01 02:59:31.037 >>> d = Differ()
2025-07-01 02:59:31.041 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:31.046 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:31.050 >>> print(''.join(results), end="")
2025-07-01 02:59:31.055 - abcDefghiJkl
2025-07-01 02:59:31.064 + abcdefGhijkl
2025-07-01 02:59:31.073 """
2025-07-01 02:59:31.078
2025-07-01 02:59:31.082 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:31.087 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:31.091 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:31.096 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:31.100 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:31.104
2025-07-01 02:59:31.109 # search for the pair that matches best without being identical
2025-07-01 02:59:31.114 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:31.118 # on junk -- unless we have to)
2025-07-01 02:59:31.123 for j in range(blo, bhi):
2025-07-01 02:59:31.128 bj = b[j]
2025-07-01 02:59:31.132 cruncher.set_seq2(bj)
2025-07-01 02:59:31.137 for i in range(alo, ahi):
2025-07-01 02:59:31.141 ai = a[i]
2025-07-01 02:59:31.145 if ai == bj:
2025-07-01 02:59:31.150 if eqi is None:
2025-07-01 02:59:31.154 eqi, eqj = i, j
2025-07-01 02:59:31.158 continue
2025-07-01 02:59:31.163 cruncher.set_seq1(ai)
2025-07-01 02:59:31.169 # computing similarity is expensive, so use the quick
2025-07-01 02:59:31.173 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:31.178 # compares by a factor of 3.
2025-07-01 02:59:31.183 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:31.188 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:31.192 # of the computation is cached by cruncher
2025-07-01 02:59:31.197 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:31.202 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:31.207 cruncher.ratio() > best_ratio:
2025-07-01 02:59:31.212 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:31.216 if best_ratio < cutoff:
2025-07-01 02:59:31.222 # no non-identical "pretty close" pair
2025-07-01 02:59:31.227 if eqi is None:
2025-07-01 02:59:31.233 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:31.238 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:31.244 return
2025-07-01 02:59:31.250 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:31.255 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:31.261 else:
2025-07-01 02:59:31.265 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:31.269 eqi = None
2025-07-01 02:59:31.274
2025-07-01 02:59:31.278 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:31.283 # identical
2025-07-01 02:59:31.287
2025-07-01 02:59:31.292 # pump out diffs from before the synch point
2025-07-01 02:59:31.296 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:31.301
2025-07-01 02:59:31.305 # do intraline marking on the synch pair
2025-07-01 02:59:31.309 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:31.314 if eqi is None:
2025-07-01 02:59:31.318 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:31.323 atags = btags = ""
2025-07-01 02:59:31.327 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:31.333 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:31.337 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:31.341 if tag == 'replace':
2025-07-01 02:59:31.346 atags += '^' * la
2025-07-01 02:59:31.351 btags += '^' * lb
2025-07-01 02:59:31.355 elif tag == 'delete':
2025-07-01 02:59:31.360 atags += '-' * la
2025-07-01 02:59:31.365 elif tag == 'insert':
2025-07-01 02:59:31.369 btags += '+' * lb
2025-07-01 02:59:31.374 elif tag == 'equal':
2025-07-01 02:59:31.378 atags += ' ' * la
2025-07-01 02:59:31.383 btags += ' ' * lb
2025-07-01 02:59:31.388 else:
2025-07-01 02:59:31.393 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:31.397 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:31.402 else:
2025-07-01 02:59:31.406 # the synch pair is identical
2025-07-01 02:59:31.411 yield '  ' + aelt
2025-07-01 02:59:31.416
2025-07-01 02:59:31.422 # pump out diffs from after the synch point
2025-07-01 02:59:31.428 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:31.434
2025-07-01 02:59:31.440 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:31.445 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:31.451
2025-07-01 02:59:31.457 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:31.463 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:31.468 alo = 32, ahi = 1101
2025-07-01 02:59:31.474 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:31.480 blo = 32, bhi = 1101
2025-07-01 02:59:31.485
2025-07-01 02:59:31.491 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:31.497 g = []
2025-07-01 02:59:31.501 if alo < ahi:
2025-07-01 02:59:31.506 if blo < bhi:
2025-07-01 02:59:31.511 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:31.516 else:
2025-07-01 02:59:31.520 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:31.525 elif blo < bhi:
2025-07-01 02:59:31.530 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:31.534
2025-07-01 02:59:31.539 >       yield from g
2025-07-01 02:59:31.545
2025-07-01 02:59:31.550 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:31.555 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:31.561
2025-07-01 02:59:31.566 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:31.571 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:31.575 alo = 32, ahi = 1101
2025-07-01 02:59:31.581 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:31.585 blo = 32, bhi = 1101
2025-07-01 02:59:31.590
2025-07-01 02:59:31.595 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:31.600 r"""
2025-07-01 02:59:31.604 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:31.609 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:31.614 synch point, and intraline difference marking is done on the
2025-07-01 02:59:31.618 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:31.624
2025-07-01 02:59:31.629 Example:
2025-07-01 02:59:31.634
2025-07-01 02:59:31.640 >>> d = Differ()
2025-07-01 02:59:31.644 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:31.649 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:31.653 >>> print(''.join(results), end="")
2025-07-01 02:59:31.658 - abcDefghiJkl
2025-07-01 02:59:31.669 + abcdefGhijkl
2025-07-01 02:59:31.682 """
2025-07-01 02:59:31.688
2025-07-01 02:59:31.695 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:31.702 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:31.708 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:31.712 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:31.717 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:31.721
2025-07-01 02:59:31.726 # search for the pair that matches best without being identical
2025-07-01 02:59:31.731 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:31.736 # on junk -- unless we have to)
2025-07-01 02:59:31.740 for j in range(blo, bhi):
2025-07-01 02:59:31.745 bj = b[j]
2025-07-01 02:59:31.750 cruncher.set_seq2(bj)
2025-07-01 02:59:31.754 for i in range(alo, ahi):
2025-07-01 02:59:31.759 ai = a[i]
2025-07-01 02:59:31.763 if ai == bj:
2025-07-01 02:59:31.768 if eqi is None:
2025-07-01 02:59:31.772 eqi, eqj = i, j
2025-07-01 02:59:31.777 continue
2025-07-01 02:59:31.781 cruncher.set_seq1(ai)
2025-07-01 02:59:31.786 # computing similarity is expensive, so use the quick
2025-07-01 02:59:31.790 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:31.795 # compares by a factor of 3.
2025-07-01 02:59:31.800 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:31.805 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:31.809 # of the computation is cached by cruncher
2025-07-01 02:59:31.814 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:31.818 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:31.823 cruncher.ratio() > best_ratio:
2025-07-01 02:59:31.828 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:31.832 if best_ratio < cutoff:
2025-07-01 02:59:31.837 # no non-identical "pretty close" pair
2025-07-01 02:59:31.841 if eqi is None:
2025-07-01 02:59:31.846 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:31.851 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:31.855 return
2025-07-01 02:59:31.860 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:31.865 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:31.869 else:
2025-07-01 02:59:31.874 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:31.878 eqi = None
2025-07-01 02:59:31.883
2025-07-01 02:59:31.887 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:31.892 # identical
2025-07-01 02:59:31.896
2025-07-01 02:59:31.901 # pump out diffs from before the synch point
2025-07-01 02:59:31.906 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:31.910
2025-07-01 02:59:31.915 # do intraline marking on the synch pair
2025-07-01 02:59:31.919 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:31.924 if eqi is None:
2025-07-01 02:59:31.929 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:31.933 atags = btags = ""
2025-07-01 02:59:31.938 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:31.943 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:31.949 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:31.953 if tag == 'replace':
2025-07-01 02:59:31.958 atags += '^' * la
2025-07-01 02:59:31.962 btags += '^' * lb
2025-07-01 02:59:31.967 elif tag == 'delete':
2025-07-01 02:59:31.972 atags += '-' * la
2025-07-01 02:59:31.976 elif tag == 'insert':
2025-07-01 02:59:31.981 btags += '+' * lb
2025-07-01 02:59:31.985 elif tag == 'equal':
2025-07-01 02:59:31.989 atags += ' ' * la
2025-07-01 02:59:31.994 btags += ' ' * lb
2025-07-01 02:59:31.998 else:
2025-07-01 02:59:32.003 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:32.007 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:32.012 else:
2025-07-01 02:59:32.016 # the synch pair is identical
2025-07-01 02:59:32.021 yield '  ' + aelt
2025-07-01 02:59:32.025
2025-07-01 02:59:32.030 # pump out diffs from after the synch point
2025-07-01 02:59:32.035 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:32.039
2025-07-01 02:59:32.044 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:32.048 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:32.052
2025-07-01 02:59:32.056 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:32.062 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:32.066 alo = 33, ahi = 1101
2025-07-01 02:59:32.071 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:32.075 blo = 33, bhi = 1101
2025-07-01 02:59:32.080
2025-07-01 02:59:32.085 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:32.089 g = []
2025-07-01 02:59:32.093 if alo < ahi:
2025-07-01 02:59:32.098 if blo < bhi:
2025-07-01 02:59:32.102 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:32.106 else:
2025-07-01 02:59:32.111 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:32.115 elif blo < bhi:
2025-07-01 02:59:32.119 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:32.124
2025-07-01 02:59:32.128 >       yield from g
2025-07-01 02:59:32.132
2025-07-01 02:59:32.137 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:32.141 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:32.145
2025-07-01 02:59:32.150 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:32.154 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:32.159 alo = 33, ahi = 1101
2025-07-01 02:59:32.164 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:32.168 blo = 33, bhi = 1101
2025-07-01 02:59:32.173
2025-07-01 02:59:32.178 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:32.182 r"""
2025-07-01 02:59:32.186 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:32.191 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:32.195 synch point, and intraline difference marking is done on the
2025-07-01 02:59:32.199 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:32.203
2025-07-01 02:59:32.208 Example:
2025-07-01 02:59:32.212
2025-07-01 02:59:32.216 >>> d = Differ()
2025-07-01 02:59:32.221 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:32.225 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:32.229 >>> print(''.join(results), end="")
2025-07-01 02:59:32.234 - abcDefghiJkl
2025-07-01 02:59:32.243 + abcdefGhijkl
2025-07-01 02:59:32.252 """
2025-07-01 02:59:32.256
2025-07-01 02:59:32.261 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:32.265 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:32.270 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:32.274 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:32.279 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:32.283
2025-07-01 02:59:32.290 # search for the pair that matches best without being identical
2025-07-01 02:59:32.295 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:32.299 # on junk -- unless we have to)
2025-07-01 02:59:32.305 for j in range(blo, bhi):
2025-07-01 02:59:32.310 bj = b[j]
2025-07-01 02:59:32.315 cruncher.set_seq2(bj)
2025-07-01 02:59:32.320 for i in range(alo, ahi):
2025-07-01 02:59:32.325 ai = a[i]
2025-07-01 02:59:32.329 if ai == bj:
2025-07-01 02:59:32.334 if eqi is None:
2025-07-01 02:59:32.339 eqi, eqj = i, j
2025-07-01 02:59:32.343 continue
2025-07-01 02:59:32.348 cruncher.set_seq1(ai)
2025-07-01 02:59:32.353 # computing similarity is expensive, so use the quick
2025-07-01 02:59:32.357 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:32.362 # compares by a factor of 3.
2025-07-01 02:59:32.367 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:32.372 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:32.376 # of the computation is cached by cruncher
2025-07-01 02:59:32.381 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:32.386 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:32.392 cruncher.ratio() > best_ratio:
2025-07-01 02:59:32.397 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:32.402 if best_ratio < cutoff:
2025-07-01 02:59:32.407 # no non-identical "pretty close" pair
2025-07-01 02:59:32.413 if eqi is None:
2025-07-01 02:59:32.417 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:32.423 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:32.428 return
2025-07-01 02:59:32.434 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:32.439 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:32.444 else:
2025-07-01 02:59:32.448 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:32.454 eqi = None
2025-07-01 02:59:32.458
2025-07-01 02:59:32.463 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:32.468 # identical
2025-07-01 02:59:32.472
2025-07-01 02:59:32.477 # pump out diffs from before the synch point
2025-07-01 02:59:32.482 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:32.487
2025-07-01 02:59:32.492 # do intraline marking on the synch pair
2025-07-01 02:59:32.497 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:32.501 if eqi is None:
2025-07-01 02:59:32.506 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:32.510 atags = btags = ""
2025-07-01 02:59:32.515 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:32.519 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:32.524 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:32.528 if tag == 'replace':
2025-07-01 02:59:32.533 atags += '^' * la
2025-07-01 02:59:32.537 btags += '^' * lb
2025-07-01 02:59:32.542 elif tag == 'delete':
2025-07-01 02:59:32.547 atags += '-' * la
2025-07-01 02:59:32.552 elif tag == 'insert':
2025-07-01 02:59:32.556 btags += '+' * lb
2025-07-01 02:59:32.561 elif tag == 'equal':
2025-07-01 02:59:32.566 atags += ' ' * la
2025-07-01 02:59:32.572 btags += ' ' * lb
2025-07-01 02:59:32.577 else:
2025-07-01 02:59:32.581 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:32.586 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:32.591 else:
2025-07-01 02:59:32.597 # the synch pair is identical
2025-07-01 02:59:32.601 yield '  ' + aelt
2025-07-01 02:59:32.606
2025-07-01 02:59:32.611 # pump out diffs from after the synch point
2025-07-01 02:59:32.617 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:32.623
2025-07-01 02:59:32.629 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:32.635 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:32.641
2025-07-01 02:59:32.645 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:32.650 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:32.655 alo = 34, ahi = 1101
2025-07-01 02:59:32.660 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:32.664 blo = 34, bhi = 1101
2025-07-01 02:59:32.669
2025-07-01 02:59:32.673 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:32.678 g = []
2025-07-01 02:59:32.682 if alo < ahi:
2025-07-01 02:59:32.687 if blo < bhi:
2025-07-01 02:59:32.693 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:32.698 else:
2025-07-01 02:59:32.703 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:32.708 elif blo < bhi:
2025-07-01 02:59:32.712 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:32.717
2025-07-01 02:59:32.721 >       yield from g
2025-07-01 02:59:32.725
2025-07-01 02:59:32.730 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:32.735 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:32.739
2025-07-01 02:59:32.744 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:32.748 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:32.753 alo = 34, ahi = 1101
2025-07-01 02:59:32.757 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:32.762 blo = 34, bhi = 1101
2025-07-01 02:59:32.766
2025-07-01 02:59:32.770 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:32.775 r"""
2025-07-01 02:59:32.779 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:32.783 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:32.788 synch point, and intraline difference marking is done on the
2025-07-01 02:59:32.792 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:32.797
2025-07-01 02:59:32.801 Example:
2025-07-01 02:59:32.806
2025-07-01 02:59:32.811 >>> d = Differ()
2025-07-01 02:59:32.815 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:32.820 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:32.824 >>> print(''.join(results), end="")
2025-07-01 02:59:32.829 - abcDefghiJkl
2025-07-01 02:59:32.838 + abcdefGhijkl
2025-07-01 02:59:32.847 """
2025-07-01 02:59:32.851
2025-07-01 02:59:32.856 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:32.861 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:32.867 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:32.872 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:32.876 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:32.881
2025-07-01 02:59:32.886 # search for the pair that matches best without being identical
2025-07-01 02:59:32.890 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:32.895 # on junk -- unless we have to)
2025-07-01 02:59:32.899 for j in range(blo, bhi):
2025-07-01 02:59:32.904 bj = b[j]
2025-07-01 02:59:32.908 cruncher.set_seq2(bj)
2025-07-01 02:59:32.913 for i in range(alo, ahi):
2025-07-01 02:59:32.918 ai = a[i]
2025-07-01 02:59:32.922 if ai == bj:
2025-07-01 02:59:32.927 if eqi is None:
2025-07-01 02:59:32.932 eqi, eqj = i, j
2025-07-01 02:59:32.936 continue
2025-07-01 02:59:32.941 cruncher.set_seq1(ai)
2025-07-01 02:59:32.945 # computing similarity is expensive, so use the quick
2025-07-01 02:59:32.950 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:32.955 # compares by a factor of 3.
2025-07-01 02:59:32.959 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:32.964 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:32.968 # of the computation is cached by cruncher
2025-07-01 02:59:32.973 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:32.978 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:32.982 cruncher.ratio() > best_ratio:
2025-07-01 02:59:32.987 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:32.992 if best_ratio < cutoff:
2025-07-01 02:59:32.997 # no non-identical "pretty close" pair
2025-07-01 02:59:33.001 if eqi is None:
2025-07-01 02:59:33.006 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:33.011 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:33.016 return
2025-07-01 02:59:33.021 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:33.026 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:33.030 else:
2025-07-01 02:59:33.035 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:33.039 eqi = None
2025-07-01 02:59:33.044
2025-07-01 02:59:33.048 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:33.052 # identical
2025-07-01 02:59:33.057
2025-07-01 02:59:33.061 # pump out diffs from before the synch point
2025-07-01 02:59:33.066 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:33.070
2025-07-01 02:59:33.075 # do intraline marking on the synch pair
2025-07-01 02:59:33.079 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:33.084 if eqi is None:
2025-07-01 02:59:33.088 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:33.093 atags = btags = ""
2025-07-01 02:59:33.097 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:33.102 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:33.106 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:33.111 if tag == 'replace':
2025-07-01 02:59:33.115 atags += '^' * la
2025-07-01 02:59:33.120 btags += '^' * lb
2025-07-01 02:59:33.124 elif tag == 'delete':
2025-07-01 02:59:33.129 atags += '-' * la
2025-07-01 02:59:33.134 elif tag == 'insert':
2025-07-01 02:59:33.139 btags += '+' * lb
2025-07-01 02:59:33.144 elif tag == 'equal':
2025-07-01 02:59:33.148 atags += ' ' * la
2025-07-01 02:59:33.153 btags += ' ' * lb
2025-07-01 02:59:33.158 else:
2025-07-01 02:59:33.162 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:33.168 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:33.172 else:
2025-07-01 02:59:33.177 # the synch pair is identical
2025-07-01 02:59:33.181 yield '  ' + aelt
2025-07-01 02:59:33.186
2025-07-01 02:59:33.191 # pump out diffs from after the synch point
2025-07-01 02:59:33.195 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:33.200
2025-07-01 02:59:33.205 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:33.209 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:33.214
2025-07-01 02:59:33.220 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:33.225 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:33.230 alo = 35, ahi = 1101
2025-07-01 02:59:33.235 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:33.240 blo = 35, bhi = 1101
2025-07-01 02:59:33.245
2025-07-01 02:59:33.250 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:33.255 g = []
2025-07-01 02:59:33.260 if alo < ahi:
2025-07-01 02:59:33.266 if blo < bhi:
2025-07-01 02:59:33.273 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:33.277 else:
2025-07-01 02:59:33.282 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:33.287 elif blo < bhi:
2025-07-01 02:59:33.292 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:33.298
2025-07-01 02:59:33.303 >       yield from g
2025-07-01 02:59:33.308
2025-07-01 02:59:33.313 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:33.317 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:33.323
2025-07-01 02:59:33.329 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:33.336 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:33.341 alo = 35, ahi = 1101
2025-07-01 02:59:33.348 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:33.354 blo = 35, bhi = 1101
2025-07-01 02:59:33.359
2025-07-01 02:59:33.365 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:33.372 r"""
2025-07-01 02:59:33.378 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:33.384 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:33.390 synch point, and intraline difference marking is done on the
2025-07-01 02:59:33.396 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:33.401
2025-07-01 02:59:33.407 Example:
2025-07-01 02:59:33.414
2025-07-01 02:59:33.420 >>> d = Differ()
2025-07-01 02:59:33.426 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:33.431 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:33.436 >>> print(''.join(results), end="")
2025-07-01 02:59:33.441 - abcDefghiJkl
2025-07-01 02:59:33.450 + abcdefGhijkl
2025-07-01 02:59:33.459 """
2025-07-01 02:59:33.463
2025-07-01 02:59:33.468 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:33.473 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:33.477 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:33.482 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:33.487 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:33.492
2025-07-01 02:59:33.496 # search for the pair that matches best without being identical
2025-07-01 02:59:33.501 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:33.506 # on junk -- unless we have to)
2025-07-01 02:59:33.510 for j in range(blo, bhi):
2025-07-01 02:59:33.515 bj = b[j]
2025-07-01 02:59:33.519 cruncher.set_seq2(bj)
2025-07-01 02:59:33.524 for i in range(alo, ahi):
2025-07-01 02:59:33.528 ai = a[i]
2025-07-01 02:59:33.533 if ai == bj:
2025-07-01 02:59:33.538 if eqi is None:
2025-07-01 02:59:33.543 eqi, eqj = i, j
2025-07-01 02:59:33.549 continue
2025-07-01 02:59:33.557 cruncher.set_seq1(ai)
2025-07-01 02:59:33.562 # computing similarity is expensive, so use the quick
2025-07-01 02:59:33.569 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:33.575 # compares by a factor of 3.
2025-07-01 02:59:33.586 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:33.596 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:33.602 # of the computation is cached by cruncher
2025-07-01 02:59:33.608 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:33.614 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:33.620 cruncher.ratio() > best_ratio:
2025-07-01 02:59:33.624 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:33.629 if best_ratio < cutoff:
2025-07-01 02:59:33.633 # no non-identical "pretty close" pair
2025-07-01 02:59:33.638 if eqi is None:
2025-07-01 02:59:33.643 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:33.647 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:33.652 return
2025-07-01 02:59:33.656 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:33.661 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:33.666 else:
2025-07-01 02:59:33.676 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:33.684 eqi = None
2025-07-01 02:59:33.690
2025-07-01 02:59:33.696 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:33.702 # identical
2025-07-01 02:59:33.708
2025-07-01 02:59:33.714 # pump out diffs from before the synch point
2025-07-01 02:59:33.720 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:33.726
2025-07-01 02:59:33.732 # do intraline marking on the synch pair
2025-07-01 02:59:33.738 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:33.746 if eqi is None:
2025-07-01 02:59:33.752 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:33.758 atags = btags = ""
2025-07-01 02:59:33.765 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:33.771 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:33.777 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:33.783 if tag == 'replace':
2025-07-01 02:59:33.790 atags += '^' * la
2025-07-01 02:59:33.796 btags += '^' * lb
2025-07-01 02:59:33.804 elif tag == 'delete':
2025-07-01 02:59:33.811 atags += '-' * la
2025-07-01 02:59:33.816 elif tag == 'insert':
2025-07-01 02:59:33.820 btags += '+' * lb
2025-07-01 02:59:33.824 elif tag == 'equal':
2025-07-01 02:59:33.830 atags += ' ' * la
2025-07-01 02:59:33.836 btags += ' ' * lb
2025-07-01 02:59:33.841 else:
2025-07-01 02:59:33.846 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:33.851 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:33.856 else:
2025-07-01 02:59:33.861 # the synch pair is identical
2025-07-01 02:59:33.866 yield '  ' + aelt
2025-07-01 02:59:33.871
2025-07-01 02:59:33.877 # pump out diffs from after the synch point
2025-07-01 02:59:33.881 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:33.885
2025-07-01 02:59:33.890 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:33.895 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:33.900
2025-07-01 02:59:33.905 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:33.909 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:33.914 alo = 36, ahi = 1101
2025-07-01 02:59:33.919 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:33.923 blo = 36, bhi = 1101
2025-07-01 02:59:33.927
2025-07-01 02:59:33.932 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:33.937 g = []
2025-07-01 02:59:33.941 if alo < ahi:
2025-07-01 02:59:33.946 if blo < bhi:
2025-07-01 02:59:33.951 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:33.955 else:
2025-07-01 02:59:33.960 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:33.964 elif blo < bhi:
2025-07-01 02:59:33.969 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:33.973
2025-07-01 02:59:33.978 >       yield from g
2025-07-01 02:59:33.982
2025-07-01 02:59:33.987 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:33.992 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:33.997
2025-07-01 02:59:34.002 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:34.007 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:34.013 alo = 36, ahi = 1101
2025-07-01 02:59:34.019 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:34.024 blo = 36, bhi = 1101
2025-07-01 02:59:34.029
2025-07-01 02:59:34.034 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:34.039 r"""
2025-07-01 02:59:34.044 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:34.050 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:34.055 synch point, and intraline difference marking is done on the
2025-07-01 02:59:34.061 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:34.065
2025-07-01 02:59:34.070 Example:
2025-07-01 02:59:34.075
2025-07-01 02:59:34.079 >>> d = Differ()
2025-07-01 02:59:34.084 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:34.089 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:34.094 >>> print(''.join(results), end="")
2025-07-01 02:59:34.098 - abcDefghiJkl
2025-07-01 02:59:34.107 + abcdefGhijkl
2025-07-01 02:59:34.116 """
2025-07-01 02:59:34.121
2025-07-01 02:59:34.126 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:34.131 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:34.135 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:34.140 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:34.145 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:34.150
2025-07-01 02:59:34.155 # search for the pair that matches best without being identical
2025-07-01 02:59:34.159 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:34.164 # on junk -- unless we have to)
2025-07-01 02:59:34.168 for j in range(blo, bhi):
2025-07-01 02:59:34.173 bj = b[j]
2025-07-01 02:59:34.177 cruncher.set_seq2(bj)
2025-07-01 02:59:34.182 for i in range(alo, ahi):
2025-07-01 02:59:34.186 ai = a[i]
2025-07-01 02:59:34.190 if ai == bj:
2025-07-01 02:59:34.195 if eqi is None:
2025-07-01 02:59:34.201 eqi, eqj = i, j
2025-07-01 02:59:34.205 continue
2025-07-01 02:59:34.210 cruncher.set_seq1(ai)
2025-07-01 02:59:34.214 # computing similarity is expensive, so use the quick
2025-07-01 02:59:34.219 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:34.224 # compares by a factor of 3.
2025-07-01 02:59:34.229 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:34.233 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:34.237 # of the computation is cached by cruncher
2025-07-01 02:59:34.242 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:34.247 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:34.251 cruncher.ratio() > best_ratio:
2025-07-01 02:59:34.256 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:34.260 if best_ratio < cutoff:
2025-07-01 02:59:34.264 # no non-identical "pretty close" pair
2025-07-01 02:59:34.269 if eqi is None:
2025-07-01 02:59:34.274 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:34.278 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:34.283 return
2025-07-01 02:59:34.288 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:34.292 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:34.296 else:
2025-07-01 02:59:34.301 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:34.306 eqi = None
2025-07-01 02:59:34.310
2025-07-01 02:59:34.315 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:34.319 # identical
2025-07-01 02:59:34.323
2025-07-01 02:59:34.329 # pump out diffs from before the synch point
2025-07-01 02:59:34.334 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:34.339
2025-07-01 02:59:34.343 # do intraline marking on the synch pair
2025-07-01 02:59:34.348 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:34.352 if eqi is None:
2025-07-01 02:59:34.357 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:34.361 atags = btags = ""
2025-07-01 02:59:34.366 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:34.370 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:34.375 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:34.379 if tag == 'replace':
2025-07-01 02:59:34.384 atags += '^' * la
2025-07-01 02:59:34.388 btags += '^' * lb
2025-07-01 02:59:34.393 elif tag == 'delete':
2025-07-01 02:59:34.398 atags += '-' * la
2025-07-01 02:59:34.402 elif tag == 'insert':
2025-07-01 02:59:34.407 btags += '+' * lb
2025-07-01 02:59:34.411 elif tag == 'equal':
2025-07-01 02:59:34.416 atags += ' ' * la
2025-07-01 02:59:34.420 btags += ' ' * lb
2025-07-01 02:59:34.425 else:
2025-07-01 02:59:34.430 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:34.435 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:34.439 else:
2025-07-01 02:59:34.444 # the synch pair is identical
2025-07-01 02:59:34.449 yield '  ' + aelt
2025-07-01 02:59:34.454
2025-07-01 02:59:34.459 # pump out diffs from after the synch point
2025-07-01 02:59:34.464 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:34.469
2025-07-01 02:59:34.473 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:34.478 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:34.482
2025-07-01 02:59:34.487 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:34.493 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:34.498 alo = 37, ahi = 1101
2025-07-01 02:59:34.503 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:34.507 blo = 37, bhi = 1101
2025-07-01 02:59:34.512
2025-07-01 02:59:34.517 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:34.521 g = []
2025-07-01 02:59:34.526 if alo < ahi:
2025-07-01 02:59:34.530 if blo < bhi:
2025-07-01 02:59:34.535 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:34.540 else:
2025-07-01 02:59:34.544 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:34.549 elif blo < bhi:
2025-07-01 02:59:34.554 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:34.559
2025-07-01 02:59:34.564 >       yield from g
2025-07-01 02:59:34.569
2025-07-01 02:59:34.573 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:34.580 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:34.586
2025-07-01 02:59:34.595 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:34.601 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:34.607 alo = 37, ahi = 1101
2025-07-01 02:59:34.614 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:34.620 blo = 37, bhi = 1101
2025-07-01 02:59:34.625
2025-07-01 02:59:34.630 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:34.636 r"""
2025-07-01 02:59:34.642 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:34.648 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:34.654 synch point, and intraline difference marking is done on the
2025-07-01 02:59:34.659 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:34.663
2025-07-01 02:59:34.667 Example:
2025-07-01 02:59:34.671
2025-07-01 02:59:34.676 >>> d = Differ()
2025-07-01 02:59:34.680 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:34.685 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:34.689 >>> print(''.join(results), end="")
2025-07-01 02:59:34.694 - abcDefghiJkl
2025-07-01 02:59:34.702 + abcdefGhijkl
2025-07-01 02:59:34.711 """
2025-07-01 02:59:34.716
2025-07-01 02:59:34.720 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:34.725 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:34.729 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:34.733 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:34.737 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:34.742
2025-07-01 02:59:34.746 # search for the pair that matches best without being identical
2025-07-01 02:59:34.751 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:34.755 # on junk -- unless we have to)
2025-07-01 02:59:34.759 for j in range(blo, bhi):
2025-07-01 02:59:34.764 bj = b[j]
2025-07-01 02:59:34.769 cruncher.set_seq2(bj)
2025-07-01 02:59:34.774 for i in range(alo, ahi):
2025-07-01 02:59:34.779 ai = a[i]
2025-07-01 02:59:34.783 if ai == bj:
2025-07-01 02:59:34.787 if eqi is None:
2025-07-01 02:59:34.792 eqi, eqj = i, j
2025-07-01 02:59:34.796 continue
2025-07-01 02:59:34.801 cruncher.set_seq1(ai)
2025-07-01 02:59:34.805 # computing similarity is expensive, so use the quick
2025-07-01 02:59:34.810 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:34.815 # compares by a factor of 3.
2025-07-01 02:59:34.820 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:34.825 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:34.830 # of the computation is cached by cruncher
2025-07-01 02:59:34.835 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:34.839 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:34.844 cruncher.ratio() > best_ratio:
2025-07-01 02:59:34.848 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:34.853 if best_ratio < cutoff:
2025-07-01 02:59:34.857 # no non-identical "pretty close" pair
2025-07-01 02:59:34.862 if eqi is None:
2025-07-01 02:59:34.867 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:34.872 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:34.877 return
2025-07-01 02:59:34.882 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:34.886 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:34.891 else:
2025-07-01 02:59:34.896 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:34.901 eqi = None
2025-07-01 02:59:34.906
2025-07-01 02:59:34.910 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:34.915 # identical
2025-07-01 02:59:34.919
2025-07-01 02:59:34.924 # pump out diffs from before the synch point
2025-07-01 02:59:34.929 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:34.933
2025-07-01 02:59:34.938 # do intraline marking on the synch pair
2025-07-01 02:59:34.942 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:34.947 if eqi is None:
2025-07-01 02:59:34.952 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:34.956 atags = btags = ""
2025-07-01 02:59:34.961 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:34.966 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:34.970 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:34.975 if tag == 'replace':
2025-07-01 02:59:34.980 atags += '^' * la
2025-07-01 02:59:34.984 btags += '^' * lb
2025-07-01 02:59:34.989 elif tag == 'delete':
2025-07-01 02:59:34.993 atags += '-' * la
2025-07-01 02:59:34.998 elif tag == 'insert':
2025-07-01 02:59:35.002 btags += '+' * lb
2025-07-01 02:59:35.007 elif tag == 'equal':
2025-07-01 02:59:35.011 atags += ' ' * la
2025-07-01 02:59:35.016 btags += ' ' * lb
2025-07-01 02:59:35.021 else:
2025-07-01 02:59:35.026 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:35.031 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:35.035 else:
2025-07-01 02:59:35.040 # the synch pair is identical
2025-07-01 02:59:35.044 yield '  ' + aelt
2025-07-01 02:59:35.049
2025-07-01 02:59:35.053 # pump out diffs from after the synch point
2025-07-01 02:59:35.057 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:35.062
2025-07-01 02:59:35.066 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:35.071 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:35.076
2025-07-01 02:59:35.081 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:35.086 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:35.091 alo = 38, ahi = 1101
2025-07-01 02:59:35.097 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:35.102 blo = 38, bhi = 1101
2025-07-01 02:59:35.107
2025-07-01 02:59:35.112 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:35.117 g = []
2025-07-01 02:59:35.122 if alo < ahi:
2025-07-01 02:59:35.128 if blo < bhi:
2025-07-01 02:59:35.134 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:35.140 else:
2025-07-01 02:59:35.145 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:35.151 elif blo < bhi:
2025-07-01 02:59:35.157 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:35.163
2025-07-01 02:59:35.168 >       yield from g
2025-07-01 02:59:35.174
2025-07-01 02:59:35.180 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:35.185 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:35.189
2025-07-01 02:59:35.194 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:35.199 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:35.204 alo = 38, ahi = 1101
2025-07-01 02:59:35.209 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:35.213 blo = 38, bhi = 1101
2025-07-01 02:59:35.217
2025-07-01 02:59:35.222 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:35.226 r"""
2025-07-01 02:59:35.231 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:35.235 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:35.239 synch point, and intraline difference marking is done on the
2025-07-01 02:59:35.244 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:35.248
2025-07-01 02:59:35.254 Example:
2025-07-01 02:59:35.258
2025-07-01 02:59:35.262 >>> d = Differ()
2025-07-01 02:59:35.267 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:35.271 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:35.276 >>> print(''.join(results), end="")
2025-07-01 02:59:35.281 - abcDefghiJkl
2025-07-01 02:59:35.290 + abcdefGhijkl
2025-07-01 02:59:35.298 """
2025-07-01 02:59:35.303
2025-07-01 02:59:35.307 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:35.312 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:35.316 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:35.320 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:35.325 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:35.329
2025-07-01 02:59:35.333 # search for the pair that matches best without being identical
2025-07-01 02:59:35.338 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:35.342 # on junk -- unless we have to)
2025-07-01 02:59:35.346 for j in range(blo, bhi):
2025-07-01 02:59:35.351 bj = b[j]
2025-07-01 02:59:35.356 cruncher.set_seq2(bj)
2025-07-01 02:59:35.360 for i in range(alo, ahi):
2025-07-01 02:59:35.365 ai = a[i]
2025-07-01 02:59:35.370 if ai == bj:
2025-07-01 02:59:35.374 if eqi is None:
2025-07-01 02:59:35.379 eqi, eqj = i, j
2025-07-01 02:59:35.383 continue
2025-07-01 02:59:35.388 cruncher.set_seq1(ai)
2025-07-01 02:59:35.392 # computing similarity is expensive, so use the quick
2025-07-01 02:59:35.397 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:35.401 # compares by a factor of 3.
2025-07-01 02:59:35.406 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:35.411 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:35.417 # of the computation is cached by cruncher
2025-07-01 02:59:35.421 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:35.426 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:35.430 cruncher.ratio() > best_ratio:
2025-07-01 02:59:35.436 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:35.441 if best_ratio < cutoff:
2025-07-01 02:59:35.446 # no non-identical "pretty close" pair
2025-07-01 02:59:35.450 if eqi is None:
2025-07-01 02:59:35.455 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:35.460 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:35.464 return
2025-07-01 02:59:35.469 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:35.473 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:35.478 else:
2025-07-01 02:59:35.483 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:35.487 eqi = None
2025-07-01 02:59:35.492
2025-07-01 02:59:35.497 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:35.501 # identical
2025-07-01 02:59:35.506
2025-07-01 02:59:35.510 # pump out diffs from before the synch point
2025-07-01 02:59:35.515 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:35.521
2025-07-01 02:59:35.526 # do intraline marking on the synch pair
2025-07-01 02:59:35.530 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:35.535 if eqi is None:
2025-07-01 02:59:35.539 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:35.544 atags = btags = ""
2025-07-01 02:59:35.548 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:35.553 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:35.557 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:35.562 if tag == 'replace':
2025-07-01 02:59:35.566 atags += '^' * la
2025-07-01 02:59:35.571 btags += '^' * lb
2025-07-01 02:59:35.575 elif tag == 'delete':
2025-07-01 02:59:35.580 atags += '-' * la
2025-07-01 02:59:35.584 elif tag == 'insert':
2025-07-01 02:59:35.589 btags += '+' * lb
2025-07-01 02:59:35.593 elif tag == 'equal':
2025-07-01 02:59:35.598 atags += ' ' * la
2025-07-01 02:59:35.602 btags += ' ' * lb
2025-07-01 02:59:35.607 else:
2025-07-01 02:59:35.612 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:35.616 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:35.621 else:
2025-07-01 02:59:35.625 # the synch pair is identical
2025-07-01 02:59:35.629 yield '  ' + aelt
2025-07-01 02:59:35.634
2025-07-01 02:59:35.639 # pump out diffs from after the synch point
2025-07-01 02:59:35.644 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:35.648
2025-07-01 02:59:35.653 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:35.657 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:35.661
2025-07-01 02:59:35.666 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:35.670 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:35.674 alo = 39, ahi = 1101
2025-07-01 02:59:35.679 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:35.684 blo = 39, bhi = 1101
2025-07-01 02:59:35.688
2025-07-01 02:59:35.692 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:35.697 g = []
2025-07-01 02:59:35.704 if alo < ahi:
2025-07-01 02:59:35.714 if blo < bhi:
2025-07-01 02:59:35.722 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:35.727 else:
2025-07-01 02:59:35.731 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:35.736 elif blo < bhi:
2025-07-01 02:59:35.740 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:35.745
2025-07-01 02:59:35.749 >       yield from g
2025-07-01 02:59:35.754
2025-07-01 02:59:35.758 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:35.763 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:35.767
2025-07-01 02:59:35.771 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:35.776 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:35.780 alo = 39, ahi = 1101
2025-07-01 02:59:35.785 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:35.790 blo = 39, bhi = 1101
2025-07-01 02:59:35.794
2025-07-01 02:59:35.799 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:35.803 r"""
2025-07-01 02:59:35.808 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:35.812 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:35.817 synch point, and intraline difference marking is done on the
2025-07-01 02:59:35.821 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:35.825
2025-07-01 02:59:35.829 Example:
2025-07-01 02:59:35.833
2025-07-01 02:59:35.838 >>> d = Differ()
2025-07-01 02:59:35.843 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:35.847 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:35.852 >>> print(''.join(results), end="")
2025-07-01 02:59:35.856 - abcDefghiJkl
2025-07-01 02:59:35.865 + abcdefGhijkl
2025-07-01 02:59:35.873 """
2025-07-01 02:59:35.878
2025-07-01 02:59:35.882 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:35.887 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:35.892 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:35.896 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:35.901 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:35.906
2025-07-01 02:59:35.910 # search for the pair that matches best without being identical
2025-07-01 02:59:35.914 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:35.919 # on junk -- unless we have to)
2025-07-01 02:59:35.923 for j in range(blo, bhi):
2025-07-01 02:59:35.927 bj = b[j]
2025-07-01 02:59:35.932 cruncher.set_seq2(bj)
2025-07-01 02:59:35.936 for i in range(alo, ahi):
2025-07-01 02:59:35.940 ai = a[i]
2025-07-01 02:59:35.945 if ai == bj:
2025-07-01 02:59:35.949 if eqi is None:
2025-07-01 02:59:35.954 eqi, eqj = i, j
2025-07-01 02:59:35.958 continue
2025-07-01 02:59:35.962 cruncher.set_seq1(ai)
2025-07-01 02:59:35.967 # computing similarity is expensive, so use the quick
2025-07-01 02:59:35.971 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:35.976 # compares by a factor of 3.
2025-07-01 02:59:35.980 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:35.985 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:35.990 # of the computation is cached by cruncher
2025-07-01 02:59:35.995 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:35.999 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:36.004 cruncher.ratio() > best_ratio:
2025-07-01 02:59:36.009 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:36.013 if best_ratio < cutoff:
2025-07-01 02:59:36.018 # no non-identical "pretty close" pair
2025-07-01 02:59:36.023 if eqi is None:
2025-07-01 02:59:36.028 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:36.034 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:36.038 return
2025-07-01 02:59:36.043 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:36.048 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:36.052 else:
2025-07-01 02:59:36.056 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:36.061 eqi = None
2025-07-01 02:59:36.066
2025-07-01 02:59:36.071 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:36.075 # identical
2025-07-01 02:59:36.080
2025-07-01 02:59:36.084 # pump out diffs from before the synch point
2025-07-01 02:59:36.089 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:36.094
2025-07-01 02:59:36.098 # do intraline marking on the synch pair
2025-07-01 02:59:36.104 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:36.108 if eqi is None:
2025-07-01 02:59:36.113 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:36.117 atags = btags = ""
2025-07-01 02:59:36.122 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:36.126 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:36.131 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:36.135 if tag == 'replace':
2025-07-01 02:59:36.140 atags += '^' * la
2025-07-01 02:59:36.144 btags += '^' * lb
2025-07-01 02:59:36.149 elif tag == 'delete':
2025-07-01 02:59:36.153 atags += '-' * la
2025-07-01 02:59:36.157 elif tag == 'insert':
2025-07-01 02:59:36.162 btags += '+' * lb
2025-07-01 02:59:36.166 elif tag == 'equal':
2025-07-01 02:59:36.170 atags += ' ' * la
2025-07-01 02:59:36.175 btags += ' ' * lb
2025-07-01 02:59:36.179 else:
2025-07-01 02:59:36.184 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:36.188 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:36.193 else:
2025-07-01 02:59:36.197 # the synch pair is identical
2025-07-01 02:59:36.201 yield '  ' + aelt
2025-07-01 02:59:36.206
2025-07-01 02:59:36.210 # pump out diffs from after the synch point
2025-07-01 02:59:36.215 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:36.219
2025-07-01 02:59:36.224 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:36.228 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:36.232
2025-07-01 02:59:36.237 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:36.242 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:36.246 alo = 40, ahi = 1101
2025-07-01 02:59:36.252 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:36.256 blo = 40, bhi = 1101
2025-07-01 02:59:36.260
2025-07-01 02:59:36.265 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:36.269 g = []
2025-07-01 02:59:36.273 if alo < ahi:
2025-07-01 02:59:36.278 if blo < bhi:
2025-07-01 02:59:36.283 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:36.287 else:
2025-07-01 02:59:36.292 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:36.296 elif blo < bhi:
2025-07-01 02:59:36.300 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:36.305
2025-07-01 02:59:36.309 >       yield from g
2025-07-01 02:59:36.313
2025-07-01 02:59:36.318 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:36.323 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:36.327
2025-07-01 02:59:36.331 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:36.336 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:36.341 alo = 40, ahi = 1101
2025-07-01 02:59:36.345 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:36.350 blo = 40, bhi = 1101
2025-07-01 02:59:36.354
2025-07-01 02:59:36.358 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:36.363 r"""
2025-07-01 02:59:36.367 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:36.372 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:36.376 synch point, and intraline difference marking is done on the
2025-07-01 02:59:36.381 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:36.385
2025-07-01 02:59:36.390 Example:
2025-07-01 02:59:36.394
2025-07-01 02:59:36.399 >>> d = Differ()
2025-07-01 02:59:36.403 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:36.408 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:36.412 >>> print(''.join(results), end="")
2025-07-01 02:59:36.417 - abcDefghiJkl
2025-07-01 02:59:36.425 + abcdefGhijkl
2025-07-01 02:59:36.434 """
2025-07-01 02:59:36.439
2025-07-01 02:59:36.443 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:36.448 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:36.452 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:36.457 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:36.461 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:36.466
2025-07-01 02:59:36.470 # search for the pair that matches best without being identical
2025-07-01 02:59:36.475 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:36.480 # on junk -- unless we have to)
2025-07-01 02:59:36.484 for j in range(blo, bhi):
2025-07-01 02:59:36.489 bj = b[j]
2025-07-01 02:59:36.493 cruncher.set_seq2(bj)
2025-07-01 02:59:36.498 for i in range(alo, ahi):
2025-07-01 02:59:36.502 ai = a[i]
2025-07-01 02:59:36.507 if ai == bj:
2025-07-01 02:59:36.511 if eqi is None:
2025-07-01 02:59:36.515 eqi, eqj = i, j
2025-07-01 02:59:36.520 continue
2025-07-01 02:59:36.525 cruncher.set_seq1(ai)
2025-07-01 02:59:36.529 # computing similarity is expensive, so use the quick
2025-07-01 02:59:36.534 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:36.538 # compares by a factor of 3.
2025-07-01 02:59:36.543 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:36.548 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:36.552 # of the computation is cached by cruncher
2025-07-01 02:59:36.557 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:36.562 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:36.566 cruncher.ratio() > best_ratio:
2025-07-01 02:59:36.571 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:36.577 if best_ratio < cutoff:
2025-07-01 02:59:36.581 # no non-identical "pretty close" pair
2025-07-01 02:59:36.586 if eqi is None:
2025-07-01 02:59:36.590 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:36.595 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:36.600 return
2025-07-01 02:59:36.605 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:36.610 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:36.614 else:
2025-07-01 02:59:36.619 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:36.624 eqi = None
2025-07-01 02:59:36.630
2025-07-01 02:59:36.636 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:36.641 # identical
2025-07-01 02:59:36.647
2025-07-01 02:59:36.653 # pump out diffs from before the synch point
2025-07-01 02:59:36.659 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:36.665
2025-07-01 02:59:36.671 # do intraline marking on the synch pair
2025-07-01 02:59:36.676 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:36.681 if eqi is None:
2025-07-01 02:59:36.685 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:36.690 atags = btags = ""
2025-07-01 02:59:36.695 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:36.699 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:36.704 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:36.709 if tag == 'replace':
2025-07-01 02:59:36.713 atags += '^' * la
2025-07-01 02:59:36.717 btags += '^' * lb
2025-07-01 02:59:36.723 elif tag == 'delete':
2025-07-01 02:59:36.728 atags += '-' * la
2025-07-01 02:59:36.732 elif tag == 'insert':
2025-07-01 02:59:36.737 btags += '+' * lb
2025-07-01 02:59:36.742 elif tag == 'equal':
2025-07-01 02:59:36.747 atags += ' ' * la
2025-07-01 02:59:36.751 btags += ' ' * lb
2025-07-01 02:59:36.756 else:
2025-07-01 02:59:36.761 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:36.766 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:36.770 else:
2025-07-01 02:59:36.775 # the synch pair is identical
2025-07-01 02:59:36.780 yield '  ' + aelt
2025-07-01 02:59:36.784
2025-07-01 02:59:36.789 # pump out diffs from after the synch point
2025-07-01 02:59:36.794 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:36.798
2025-07-01 02:59:36.803 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:36.808 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:36.812
2025-07-01 02:59:36.818 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:36.824 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:36.830 alo = 41, ahi = 1101
2025-07-01 02:59:36.836 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:36.841 blo = 41, bhi = 1101
2025-07-01 02:59:36.845
2025-07-01 02:59:36.850 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:36.855 g = []
2025-07-01 02:59:36.860 if alo < ahi:
2025-07-01 02:59:36.864 if blo < bhi:
2025-07-01 02:59:36.869 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:36.873 else:
2025-07-01 02:59:36.878 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:36.882 elif blo < bhi:
2025-07-01 02:59:36.887 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:36.891
2025-07-01 02:59:36.896 >       yield from g
2025-07-01 02:59:36.901
2025-07-01 02:59:36.905 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:36.910 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:36.916
2025-07-01 02:59:36.921 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:36.926 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:36.930 alo = 41, ahi = 1101
2025-07-01 02:59:36.935 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:36.939 blo = 41, bhi = 1101
2025-07-01 02:59:36.943
2025-07-01 02:59:36.948 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:36.953 r"""
2025-07-01 02:59:36.957 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:36.962 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:36.966 synch point, and intraline difference marking is done on the
2025-07-01 02:59:36.971 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:36.977
2025-07-01 02:59:36.981 Example:
2025-07-01 02:59:36.985
2025-07-01 02:59:36.990 >>> d = Differ()
2025-07-01 02:59:36.994 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:36.999 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:37.004 >>> print(''.join(results), end="")
2025-07-01 02:59:37.009 - abcDefghiJkl
2025-07-01 02:59:37.017 + abcdefGhijkl
2025-07-01 02:59:37.026 """
2025-07-01 02:59:37.031
2025-07-01 02:59:37.036 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:37.042 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:37.047 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:37.052 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:37.057 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:37.061
2025-07-01 02:59:37.066 # search for the pair that matches best without being identical
2025-07-01 02:59:37.070 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:37.075 # on junk -- unless we have to)
2025-07-01 02:59:37.079 for j in range(blo, bhi):
2025-07-01 02:59:37.084 bj = b[j]
2025-07-01 02:59:37.088 cruncher.set_seq2(bj)
2025-07-01 02:59:37.093 for i in range(alo, ahi):
2025-07-01 02:59:37.099 ai = a[i]
2025-07-01 02:59:37.104 if ai == bj:
2025-07-01 02:59:37.109 if eqi is None:
2025-07-01 02:59:37.113 eqi, eqj = i, j
2025-07-01 02:59:37.118 continue
2025-07-01 02:59:37.122 cruncher.set_seq1(ai)
2025-07-01 02:59:37.127 # computing similarity is expensive, so use the quick
2025-07-01 02:59:37.132 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:37.136 # compares by a factor of 3.
2025-07-01 02:59:37.141 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:37.146 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:37.151 # of the computation is cached by cruncher
2025-07-01 02:59:37.155 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:37.160 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:37.164 cruncher.ratio() > best_ratio:
2025-07-01 02:59:37.169 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:37.173 if best_ratio < cutoff:
2025-07-01 02:59:37.177 # no non-identical "pretty close" pair
2025-07-01 02:59:37.182 if eqi is None:
2025-07-01 02:59:37.186 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:37.191 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:37.195 return
2025-07-01 02:59:37.200 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:37.205 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:37.210 else:
2025-07-01 02:59:37.214 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:37.219 eqi = None
2025-07-01 02:59:37.224
2025-07-01 02:59:37.229 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:37.233 # identical
2025-07-01 02:59:37.238
2025-07-01 02:59:37.243 # pump out diffs from before the synch point
2025-07-01 02:59:37.248 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:37.252
2025-07-01 02:59:37.257 # do intraline marking on the synch pair
2025-07-01 02:59:37.261 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:37.266 if eqi is None:
2025-07-01 02:59:37.271 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:37.276 atags = btags = ""
2025-07-01 02:59:37.280 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:37.285 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:37.290 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:37.295 if tag == 'replace':
2025-07-01 02:59:37.299 atags += '^' * la
2025-07-01 02:59:37.304 btags += '^' * lb
2025-07-01 02:59:37.308 elif tag == 'delete':
2025-07-01 02:59:37.312 atags += '-' * la
2025-07-01 02:59:37.317 elif tag == 'insert':
2025-07-01 02:59:37.321 btags += '+' * lb
2025-07-01 02:59:37.326 elif tag == 'equal':
2025-07-01 02:59:37.330 atags += ' ' * la
2025-07-01 02:59:37.335 btags += ' ' * lb
2025-07-01 02:59:37.339 else:
2025-07-01 02:59:37.344 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:37.350 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:37.356 else:
2025-07-01 02:59:37.362 # the synch pair is identical
2025-07-01 02:59:37.368 yield '  ' + aelt
2025-07-01 02:59:37.374
2025-07-01 02:59:37.380 # pump out diffs from after the synch point
2025-07-01 02:59:37.386 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:37.392
2025-07-01 02:59:37.398 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:37.403 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:37.407
2025-07-01 02:59:37.412 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:37.416 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:37.421 alo = 42, ahi = 1101
2025-07-01 02:59:37.425 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:37.430 blo = 42, bhi = 1101
2025-07-01 02:59:37.434
2025-07-01 02:59:37.438 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:37.443 g = []
2025-07-01 02:59:37.448 if alo < ahi:
2025-07-01 02:59:37.452 if blo < bhi:
2025-07-01 02:59:37.457 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:37.463 else:
2025-07-01 02:59:37.470 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:37.476 elif blo < bhi:
2025-07-01 02:59:37.483 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:37.490
2025-07-01 02:59:37.497 >       yield from g
2025-07-01 02:59:37.502
2025-07-01 02:59:37.508 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:37.514 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:37.519
2025-07-01 02:59:37.524 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:37.530 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:37.534 alo = 42, ahi = 1101
2025-07-01 02:59:37.540 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:37.544 blo = 42, bhi = 1101
2025-07-01 02:59:37.549
2025-07-01 02:59:37.554 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:37.558 r"""
2025-07-01 02:59:37.563 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:37.568 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:37.572 synch point, and intraline difference marking is done on the
2025-07-01 02:59:37.577 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:37.581
2025-07-01 02:59:37.586 Example:
2025-07-01 02:59:37.590
2025-07-01 02:59:37.594 >>> d = Differ()
2025-07-01 02:59:37.599 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:37.603 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:37.608 >>> print(''.join(results), end="")
2025-07-01 02:59:37.612 - abcDefghiJkl
2025-07-01 02:59:37.620 + abcdefGhijkl
2025-07-01 02:59:37.629 """
2025-07-01 02:59:37.633
2025-07-01 02:59:37.638 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:37.642 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:37.646 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:37.651 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:37.656 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:37.660
2025-07-01 02:59:37.664 # search for the pair that matches best without being identical
2025-07-01 02:59:37.669 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:37.673 # on junk -- unless we have to)
2025-07-01 02:59:37.678 for j in range(blo, bhi):
2025-07-01 02:59:37.682 bj = b[j]
2025-07-01 02:59:37.686 cruncher.set_seq2(bj)
2025-07-01 02:59:37.692 for i in range(alo, ahi):
2025-07-01 02:59:37.697 ai = a[i]
2025-07-01 02:59:37.701 if ai == bj:
2025-07-01 02:59:37.705 if eqi is None:
2025-07-01 02:59:37.710 eqi, eqj = i, j
2025-07-01 02:59:37.714 continue
2025-07-01 02:59:37.718 cruncher.set_seq1(ai)
2025-07-01 02:59:37.723 # computing similarity is expensive, so use the quick
2025-07-01 02:59:37.728 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:37.732 # compares by a factor of 3.
2025-07-01 02:59:37.737 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:37.741 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:37.746 # of the computation is cached by cruncher
2025-07-01 02:59:37.750 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:37.755 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:37.760 cruncher.ratio() > best_ratio:
2025-07-01 02:59:37.764 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:37.769 if best_ratio < cutoff:
2025-07-01 02:59:37.774 # no non-identical "pretty close" pair
2025-07-01 02:59:37.779 if eqi is None:
2025-07-01 02:59:37.783 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:37.788 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:37.792 return
2025-07-01 02:59:37.797 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:37.802 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:37.807 else:
2025-07-01 02:59:37.811 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:37.817 eqi = None
2025-07-01 02:59:37.821
2025-07-01 02:59:37.825 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:37.830 # identical
2025-07-01 02:59:37.834
2025-07-01 02:59:37.838 # pump out diffs from before the synch point
2025-07-01 02:59:37.844 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:37.848
2025-07-01 02:59:37.853 # do intraline marking on the synch pair
2025-07-01 02:59:37.857 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:37.861 if eqi is None:
2025-07-01 02:59:37.866 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:37.871 atags = btags = ""
2025-07-01 02:59:37.875 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:37.881 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:37.886 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:37.891 if tag == 'replace':
2025-07-01 02:59:37.896 atags += '^' * la
2025-07-01 02:59:37.901 btags += '^' * lb
2025-07-01 02:59:37.905 elif tag == 'delete':
2025-07-01 02:59:37.910 atags += '-' * la
2025-07-01 02:59:37.915 elif tag == 'insert':
2025-07-01 02:59:37.920 btags += '+' * lb
2025-07-01 02:59:37.925 elif tag == 'equal':
2025-07-01 02:59:37.929 atags += ' ' * la
2025-07-01 02:59:37.935 btags += ' ' * lb
2025-07-01 02:59:37.941 else:
2025-07-01 02:59:37.946 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:37.951 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:37.956 else:
2025-07-01 02:59:37.961 # the synch pair is identical
2025-07-01 02:59:37.966 yield '  ' + aelt
2025-07-01 02:59:37.970
2025-07-01 02:59:37.975 # pump out diffs from after the synch point
2025-07-01 02:59:37.979 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:37.984
2025-07-01 02:59:37.992 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:38.000 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:38.004
2025-07-01 02:59:38.009 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:38.015 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:38.020 alo = 43, ahi = 1101
2025-07-01 02:59:38.025 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:38.030 blo = 43, bhi = 1101
2025-07-01 02:59:38.035
2025-07-01 02:59:38.040 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:38.044 g = []
2025-07-01 02:59:38.049 if alo < ahi:
2025-07-01 02:59:38.053 if blo < bhi:
2025-07-01 02:59:38.058 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:38.062 else:
2025-07-01 02:59:38.067 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:38.072 elif blo < bhi:
2025-07-01 02:59:38.078 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:38.082
2025-07-01 02:59:38.087 >       yield from g
2025-07-01 02:59:38.091
2025-07-01 02:59:38.095 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:38.100 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:38.104
2025-07-01 02:59:38.108 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:38.113 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:38.117 alo = 43, ahi = 1101
2025-07-01 02:59:38.122 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:38.126 blo = 43, bhi = 1101
2025-07-01 02:59:38.131
2025-07-01 02:59:38.135 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:38.139 r"""
2025-07-01 02:59:38.144 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:38.148 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:38.153 synch point, and intraline difference marking is done on the
2025-07-01 02:59:38.158 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:38.162
2025-07-01 02:59:38.170 Example:
2025-07-01 02:59:38.176
2025-07-01 02:59:38.180 >>> d = Differ()
2025-07-01 02:59:38.185 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:38.190 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:38.194 >>> print(''.join(results), end="")
2025-07-01 02:59:38.198 - abcDefghiJkl
2025-07-01 02:59:38.207 + abcdefGhijkl
2025-07-01 02:59:38.215 """
2025-07-01 02:59:38.219
2025-07-01 02:59:38.224 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:38.228 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:38.233 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:38.237 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:38.241 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:38.246
2025-07-01 02:59:38.250 # search for the pair that matches best without being identical
2025-07-01 02:59:38.255 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:38.259 # on junk -- unless we have to)
2025-07-01 02:59:38.263 for j in range(blo, bhi):
2025-07-01 02:59:38.268 bj = b[j]
2025-07-01 02:59:38.272 cruncher.set_seq2(bj)
2025-07-01 02:59:38.276 for i in range(alo, ahi):
2025-07-01 02:59:38.281 ai = a[i]
2025-07-01 02:59:38.285 if ai == bj:
2025-07-01 02:59:38.291 if eqi is None:
2025-07-01 02:59:38.297 eqi, eqj = i, j
2025-07-01 02:59:38.302 continue
2025-07-01 02:59:38.307 cruncher.set_seq1(ai)
2025-07-01 02:59:38.311 # computing similarity is expensive, so use the quick
2025-07-01 02:59:38.316 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:38.320 # compares by a factor of 3.
2025-07-01 02:59:38.325 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:38.329 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:38.334 # of the computation is cached by cruncher
2025-07-01 02:59:38.338 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:38.342 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:38.347 cruncher.ratio() > best_ratio:
2025-07-01 02:59:38.351 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:38.356 if best_ratio < cutoff:
2025-07-01 02:59:38.360 # no non-identical "pretty close" pair
2025-07-01 02:59:38.364 if eqi is None:
2025-07-01 02:59:38.369 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:38.373 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:38.378 return
2025-07-01 02:59:38.382 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:38.387 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:38.392 else:
2025-07-01 02:59:38.397 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:38.401 eqi = None
2025-07-01 02:59:38.406
2025-07-01 02:59:38.410 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:38.414 # identical
2025-07-01 02:59:38.419
2025-07-01 02:59:38.423 # pump out diffs from before the synch point
2025-07-01 02:59:38.428 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:38.432
2025-07-01 02:59:38.437 # do intraline marking on the synch pair
2025-07-01 02:59:38.441 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:38.446 if eqi is None:
2025-07-01 02:59:38.450 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:38.455 atags = btags = ""
2025-07-01 02:59:38.459 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:38.463 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:38.468 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:38.473 if tag == 'replace':
2025-07-01 02:59:38.477 atags += '^' * la
2025-07-01 02:59:38.482 btags += '^' * lb
2025-07-01 02:59:38.487 elif tag == 'delete':
2025-07-01 02:59:38.494 atags += '-' * la
2025-07-01 02:59:38.498 elif tag == 'insert':
2025-07-01 02:59:38.503 btags += '+' * lb
2025-07-01 02:59:38.507 elif tag == 'equal':
2025-07-01 02:59:38.512 atags += ' ' * la
2025-07-01 02:59:38.517 btags += ' ' * lb
2025-07-01 02:59:38.521 else:
2025-07-01 02:59:38.525 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:38.530 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:38.534 else:
2025-07-01 02:59:38.538 # the synch pair is identical
2025-07-01 02:59:38.543 yield '  ' + aelt
2025-07-01 02:59:38.547
2025-07-01 02:59:38.552 # pump out diffs from after the synch point
2025-07-01 02:59:38.556 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:38.561
2025-07-01 02:59:38.565 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:38.570 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:38.575
2025-07-01 02:59:38.579 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:38.584 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:38.589 alo = 44, ahi = 1101
2025-07-01 02:59:38.594 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:38.599 blo = 44, bhi = 1101
2025-07-01 02:59:38.604
2025-07-01 02:59:38.609 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:38.613 g = []
2025-07-01 02:59:38.618 if alo < ahi:
2025-07-01 02:59:38.625 if blo < bhi:
2025-07-01 02:59:38.632 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:38.637 else:
2025-07-01 02:59:38.641 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:38.646 elif blo < bhi:
2025-07-01 02:59:38.650 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:38.654
2025-07-01 02:59:38.663 >       yield from g
2025-07-01 02:59:38.675
2025-07-01 02:59:38.683 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:38.690 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:38.704
2025-07-01 02:59:38.714 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:38.734 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:38.745 alo = 44, ahi = 1101
2025-07-01 02:59:38.758 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:38.774 blo = 44, bhi = 1101
2025-07-01 02:59:38.786
2025-07-01 02:59:38.798 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:38.810 r"""
2025-07-01 02:59:38.818 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:38.831 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:38.846 synch point, and intraline difference marking is done on the
2025-07-01 02:59:38.858 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:38.866
2025-07-01 02:59:38.878 Example:
2025-07-01 02:59:38.890
2025-07-01 02:59:38.899 >>> d = Differ()
2025-07-01 02:59:38.914 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:38.926 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:38.934 >>> print(''.join(results), end="")
2025-07-01 02:59:38.946 - abcDefghiJkl
2025-07-01 02:59:38.970 + abcdefGhijkl
2025-07-01 02:59:38.998 """
2025-07-01 02:59:39.010
2025-07-01 02:59:39.018 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:39.034 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:39.042 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:39.050 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:39.058 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:39.069
2025-07-01 02:59:39.078 # search for the pair that matches best without being identical
2025-07-01 02:59:39.094 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:39.102 # on junk -- unless we have to)
2025-07-01 02:59:39.114 for j in range(blo, bhi):
2025-07-01 02:59:39.122 bj = b[j]
2025-07-01 02:59:39.134 cruncher.set_seq2(bj)
2025-07-01 02:59:39.148 for i in range(alo, ahi):
2025-07-01 02:59:39.157 ai = a[i]
2025-07-01 02:59:39.167 if ai == bj:
2025-07-01 02:59:39.178 if eqi is None:
2025-07-01 02:59:39.197 eqi, eqj = i, j
2025-07-01 02:59:39.208 continue
2025-07-01 02:59:39.222 cruncher.set_seq1(ai)
2025-07-01 02:59:39.237 # computing similarity is expensive, so use the quick
2025-07-01 02:59:39.249 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:39.260 # compares by a factor of 3.
2025-07-01 02:59:39.269 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:39.286 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:39.298 # of the computation is cached by cruncher
2025-07-01 02:59:39.310 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:39.318 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:39.334 cruncher.ratio() > best_ratio:
2025-07-01 02:59:39.346 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:39.354 if best_ratio < cutoff:
2025-07-01 02:59:39.366 # no non-identical "pretty close" pair
2025-07-01 02:59:39.377 if eqi is None:
2025-07-01 02:59:39.385 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:39.397 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:39.413 return
2025-07-01 02:59:39.426 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:39.431 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:39.450 else:
2025-07-01 02:59:39.462 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:39.474 eqi = None
2025-07-01 02:59:39.485
2025-07-01 02:59:39.502 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:39.512 # identical
2025-07-01 02:59:39.522
2025-07-01 02:59:39.534 # pump out diffs from before the synch point
2025-07-01 02:59:39.546 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:39.552
2025-07-01 02:59:39.566 # do intraline marking on the synch pair
2025-07-01 02:59:39.578 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:39.585 if eqi is None:
2025-07-01 02:59:39.597 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:39.605 atags = btags = ""
2025-07-01 02:59:39.618 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:39.625 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:39.642 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:39.650 if tag == 'replace':
2025-07-01 02:59:39.662 atags += '^' * la
2025-07-01 02:59:39.669 btags += '^' * lb
2025-07-01 02:59:39.682 elif tag == 'delete':
2025-07-01 02:59:39.688 atags += '-' * la
2025-07-01 02:59:39.702 elif tag == 'insert':
2025-07-01 02:59:39.714 btags += '+' * lb
2025-07-01 02:59:39.722 elif tag == 'equal':
2025-07-01 02:59:39.737 atags += ' ' * la
2025-07-01 02:59:39.750 btags += ' ' * lb
2025-07-01 02:59:39.758 else:
2025-07-01 02:59:39.776 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:39.788 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:39.800 else:
2025-07-01 02:59:39.810 # the synch pair is identical
2025-07-01 02:59:39.817 yield '  ' + aelt
2025-07-01 02:59:39.830
2025-07-01 02:59:39.838 # pump out diffs from after the synch point
2025-07-01 02:59:39.857 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:39.864
2025-07-01 02:59:39.878 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:39.886 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:39.898
2025-07-01 02:59:39.910 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:39.926 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:39.938 alo = 45, ahi = 1101
2025-07-01 02:59:39.946 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:39.958 blo = 45, bhi = 1101
2025-07-01 02:59:39.962
2025-07-01 02:59:39.967 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:39.982 g = []
2025-07-01 02:59:39.990 if alo < ahi:
2025-07-01 02:59:40.002 if blo < bhi:
2025-07-01 02:59:40.014 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:40.022 else:
2025-07-01 02:59:40.034 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:40.042 elif blo < bhi:
2025-07-01 02:59:40.054 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:40.062
2025-07-01 02:59:40.072 >       yield from g
2025-07-01 02:59:40.085
2025-07-01 02:59:40.102 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:40.113 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:40.130
2025-07-01 02:59:40.142 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:40.152 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:40.166 alo = 45, ahi = 1101
2025-07-01 02:59:40.182 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:40.193 blo = 45, bhi = 1101
2025-07-01 02:59:40.210
2025-07-01 02:59:40.222 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:40.229 r"""
2025-07-01 02:59:40.242 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:40.253 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:40.261 synch point, and intraline difference marking is done on the
2025-07-01 02:59:40.265 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:40.270
2025-07-01 02:59:40.274 Example:
2025-07-01 02:59:40.279
2025-07-01 02:59:40.283 >>> d = Differ()
2025-07-01 02:59:40.287 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:40.291 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:40.295 >>> print(''.join(results), end="")
2025-07-01 02:59:40.300 - abcDefghiJkl
2025-07-01 02:59:40.309 + abcdefGhijkl
2025-07-01 02:59:40.317 """
2025-07-01 02:59:40.321
2025-07-01 02:59:40.326 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:40.332 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:40.338 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:40.344 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:40.351 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:40.357
2025-07-01 02:59:40.363 # search for the pair that matches best without being identical
2025-07-01 02:59:40.370 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:40.376 # on junk -- unless we have to)
2025-07-01 02:59:40.382 for j in range(blo, bhi):
2025-07-01 02:59:40.386 bj = b[j]
2025-07-01 02:59:40.392 cruncher.set_seq2(bj)
2025-07-01 02:59:40.396 for i in range(alo, ahi):
2025-07-01 02:59:40.401 ai = a[i]
2025-07-01 02:59:40.405 if ai == bj:
2025-07-01 02:59:40.409 if eqi is None:
2025-07-01 02:59:40.414 eqi, eqj = i, j
2025-07-01 02:59:40.418 continue
2025-07-01 02:59:40.422 cruncher.set_seq1(ai)
2025-07-01 02:59:40.427 # computing similarity is expensive, so use the quick
2025-07-01 02:59:40.431 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:40.436 # compares by a factor of 3.
2025-07-01 02:59:40.441 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:40.446 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:40.450 # of the computation is cached by cruncher
2025-07-01 02:59:40.454 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:40.459 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:40.464 cruncher.ratio() > best_ratio:
2025-07-01 02:59:40.469 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:40.473 if best_ratio < cutoff:
2025-07-01 02:59:40.477 # no non-identical "pretty close" pair
2025-07-01 02:59:40.481 if eqi is None:
2025-07-01 02:59:40.486 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:40.490 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:40.495 return
2025-07-01 02:59:40.499 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:40.504 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:40.517 else:
2025-07-01 02:59:40.530 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:40.542 eqi = None
2025-07-01 02:59:40.552
2025-07-01 02:59:40.562 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:40.574 # identical
2025-07-01 02:59:40.583
2025-07-01 02:59:40.590 # pump out diffs from before the synch point
2025-07-01 02:59:40.602 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:40.614
2025-07-01 02:59:40.621 # do intraline marking on the synch pair
2025-07-01 02:59:40.634 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:40.642 if eqi is None:
2025-07-01 02:59:40.649 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:40.658 atags = btags = ""
2025-07-01 02:59:40.670 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:40.678 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:40.686 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:40.694 if tag == 'replace':
2025-07-01 02:59:40.710 atags += '^' * la
2025-07-01 02:59:40.720 btags += '^' * lb
2025-07-01 02:59:40.731 elif tag == 'delete':
2025-07-01 02:59:40.736 atags += '-' * la
2025-07-01 02:59:40.741 elif tag == 'insert':
2025-07-01 02:59:40.745 btags += '+' * lb
2025-07-01 02:59:40.750 elif tag == 'equal':
2025-07-01 02:59:40.755 atags += ' ' * la
2025-07-01 02:59:40.770 btags += ' ' * lb
2025-07-01 02:59:40.784 else:
2025-07-01 02:59:40.798 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:40.809 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:40.822 else:
2025-07-01 02:59:40.834 # the synch pair is identical
2025-07-01 02:59:40.846 yield '  ' + aelt
2025-07-01 02:59:40.859
2025-07-01 02:59:40.874 # pump out diffs from after the synch point
2025-07-01 02:59:40.889 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:40.898
2025-07-01 02:59:40.910 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:40.922 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:40.934
2025-07-01 02:59:40.946 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:40.958 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:40.970 alo = 48, ahi = 1101
2025-07-01 02:59:40.978 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:40.994 blo = 48, bhi = 1101
2025-07-01 02:59:41.002
2025-07-01 02:59:41.012 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:41.029 g = []
2025-07-01 02:59:41.046 if alo < ahi:
2025-07-01 02:59:41.052 if blo < bhi:
2025-07-01 02:59:41.057 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:41.078 else:
2025-07-01 02:59:41.086 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:41.098 elif blo < bhi:
2025-07-01 02:59:41.106 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:41.118
2025-07-01 02:59:41.134 >       yield from g
2025-07-01 02:59:41.143
2025-07-01 02:59:41.154 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:41.161 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:41.165
2025-07-01 02:59:41.169 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:41.175 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:41.179 alo = 48, ahi = 1101
2025-07-01 02:59:41.184 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:41.189 blo = 48, bhi = 1101
2025-07-01 02:59:41.193
2025-07-01 02:59:41.198 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:41.202 r"""
2025-07-01 02:59:41.207 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:41.212 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:41.216 synch point, and intraline difference marking is done on the
2025-07-01 02:59:41.221 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:41.225
2025-07-01 02:59:41.229 Example:
2025-07-01 02:59:41.233
2025-07-01 02:59:41.238 >>> d = Differ()
2025-07-01 02:59:41.242 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:41.248 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:41.252 >>> print(''.join(results), end="")
2025-07-01 02:59:41.257 - abcDefghiJkl
2025-07-01 02:59:41.266 + abcdefGhijkl
2025-07-01 02:59:41.275 """
2025-07-01 02:59:41.279
2025-07-01 02:59:41.284 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:41.289 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:41.294 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:41.299 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:41.306 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:41.318
2025-07-01 02:59:41.330 # search for the pair that matches best without being identical
2025-07-01 02:59:41.342 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:41.354 # on junk -- unless we have to)
2025-07-01 02:59:41.366 for j in range(blo, bhi):
2025-07-01 02:59:41.378 bj = b[j]
2025-07-01 02:59:41.389 cruncher.set_seq2(bj)
2025-07-01 02:59:41.401 for i in range(alo, ahi):
2025-07-01 02:59:41.417 ai = a[i]
2025-07-01 02:59:41.426 if ai == bj:
2025-07-01 02:59:41.438 if eqi is None:
2025-07-01 02:59:41.458 eqi, eqj = i, j
2025-07-01 02:59:41.469 continue
2025-07-01 02:59:41.485 cruncher.set_seq1(ai)
2025-07-01 02:59:41.497 # computing similarity is expensive, so use the quick
2025-07-01 02:59:41.506 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:41.522 # compares by a factor of 3.
2025-07-01 02:59:41.530 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:41.546 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:41.558 # of the computation is cached by cruncher
2025-07-01 02:59:41.570 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:41.582 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:41.598 cruncher.ratio() > best_ratio:
2025-07-01 02:59:41.610 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:41.622 if best_ratio < cutoff:
2025-07-01 02:59:41.634 # no non-identical "pretty close" pair
2025-07-01 02:59:41.649 if eqi is None:
2025-07-01 02:59:41.662 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:41.678 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:41.690 return
2025-07-01 02:59:41.702 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:41.714 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:41.722 else:
2025-07-01 02:59:41.734 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:41.742 eqi = None
2025-07-01 02:59:41.754
2025-07-01 02:59:41.764 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:41.775 # identical
2025-07-01 02:59:41.793
2025-07-01 02:59:41.805 # pump out diffs from before the synch point
2025-07-01 02:59:41.820 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:41.833
2025-07-01 02:59:41.844 # do intraline marking on the synch pair
2025-07-01 02:59:41.855 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:41.869 if eqi is None:
2025-07-01 02:59:41.876 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:41.889 atags = btags = ""
2025-07-01 02:59:41.894 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:41.898 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:41.903 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:41.907 if tag == 'replace':
2025-07-01 02:59:41.912 atags += '^' * la
2025-07-01 02:59:41.916 btags += '^' * lb
2025-07-01 02:59:41.920 elif tag == 'delete':
2025-07-01 02:59:41.925 atags += '-' * la
2025-07-01 02:59:41.929 elif tag == 'insert':
2025-07-01 02:59:41.934 btags += '+' * lb
2025-07-01 02:59:41.938 elif tag == 'equal':
2025-07-01 02:59:41.943 atags += ' ' * la
2025-07-01 02:59:41.947 btags += ' ' * lb
2025-07-01 02:59:41.952 else:
2025-07-01 02:59:41.956 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:41.961 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:41.965 else:
2025-07-01 02:59:41.970 # the synch pair is identical
2025-07-01 02:59:41.974 yield '  ' + aelt
2025-07-01 02:59:41.978
2025-07-01 02:59:41.983 # pump out diffs from after the synch point
2025-07-01 02:59:41.987 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:41.992
2025-07-01 02:59:41.996 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:42.001 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:42.006
2025-07-01 02:59:42.012 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:42.018 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:42.022 alo = 49, ahi = 1101
2025-07-01 02:59:42.028 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:42.032 blo = 49, bhi = 1101
2025-07-01 02:59:42.037
2025-07-01 02:59:42.041 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:42.045 g = []
2025-07-01 02:59:42.050 if alo < ahi:
2025-07-01 02:59:42.054 if blo < bhi:
2025-07-01 02:59:42.058 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:42.063 else:
2025-07-01 02:59:42.067 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:42.071 elif blo < bhi:
2025-07-01 02:59:42.076 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:42.080
2025-07-01 02:59:42.085 >       yield from g
2025-07-01 02:59:42.089
2025-07-01 02:59:42.094 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:42.098 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:42.102
2025-07-01 02:59:42.107 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:42.112 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:42.117 alo = 49, ahi = 1101
2025-07-01 02:59:42.122 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:42.126 blo = 49, bhi = 1101
2025-07-01 02:59:42.131
2025-07-01 02:59:42.135 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:42.140 r"""
2025-07-01 02:59:42.145 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:42.149 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:42.154 synch point, and intraline difference marking is done on the
2025-07-01 02:59:42.158 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:42.165
2025-07-01 02:59:42.169 Example:
2025-07-01 02:59:42.173
2025-07-01 02:59:42.178 >>> d = Differ()
2025-07-01 02:59:42.182 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:42.187 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:42.191 >>> print(''.join(results), end="")
2025-07-01 02:59:42.196 - abcDefghiJkl
2025-07-01 02:59:42.205 + abcdefGhijkl
2025-07-01 02:59:42.214 """
2025-07-01 02:59:42.220
2025-07-01 02:59:42.224 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:42.229 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:42.233 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:42.238 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:42.242 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:42.246
2025-07-01 02:59:42.251 # search for the pair that matches best without being identical
2025-07-01 02:59:42.255 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:42.260 # on junk -- unless we have to)
2025-07-01 02:59:42.264 for j in range(blo, bhi):
2025-07-01 02:59:42.268 bj = b[j]
2025-07-01 02:59:42.273 cruncher.set_seq2(bj)
2025-07-01 02:59:42.277 for i in range(alo, ahi):
2025-07-01 02:59:42.281 ai = a[i]
2025-07-01 02:59:42.286 if ai == bj:
2025-07-01 02:59:42.290 if eqi is None:
2025-07-01 02:59:42.294 eqi, eqj = i, j
2025-07-01 02:59:42.299 continue
2025-07-01 02:59:42.305 cruncher.set_seq1(ai)
2025-07-01 02:59:42.310 # computing similarity is expensive, so use the quick
2025-07-01 02:59:42.315 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:42.320 # compares by a factor of 3.
2025-07-01 02:59:42.325 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:42.329 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:42.334 # of the computation is cached by cruncher
2025-07-01 02:59:42.338 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:42.343 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:42.347 cruncher.ratio() > best_ratio:
2025-07-01 02:59:42.352 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:42.356 if best_ratio < cutoff:
2025-07-01 02:59:42.361 # no non-identical "pretty close" pair
2025-07-01 02:59:42.365 if eqi is None:
2025-07-01 02:59:42.370 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:42.374 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:42.379 return
2025-07-01 02:59:42.384 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:42.389 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:42.393 else:
2025-07-01 02:59:42.397 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:42.403 eqi = None
2025-07-01 02:59:42.408
2025-07-01 02:59:42.414 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:42.419 # identical
2025-07-01 02:59:42.424
2025-07-01 02:59:42.430 # pump out diffs from before the synch point
2025-07-01 02:59:42.436 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:42.441
2025-07-01 02:59:42.446 # do intraline marking on the synch pair
2025-07-01 02:59:42.450 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:42.456 if eqi is None:
2025-07-01 02:59:42.464 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:42.471 atags = btags = ""
2025-07-01 02:59:42.477 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:42.482 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:42.487 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:42.491 if tag == 'replace':
2025-07-01 02:59:42.496 atags += '^' * la
2025-07-01 02:59:42.500 btags += '^' * lb
2025-07-01 02:59:42.504 elif tag == 'delete':
2025-07-01 02:59:42.509 atags += '-' * la
2025-07-01 02:59:42.513 elif tag == 'insert':
2025-07-01 02:59:42.518 btags += '+' * lb
2025-07-01 02:59:42.522 elif tag == 'equal':
2025-07-01 02:59:42.526 atags += ' ' * la
2025-07-01 02:59:42.530 btags += ' ' * lb
2025-07-01 02:59:42.535 else:
2025-07-01 02:59:42.539 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:42.544 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:42.549 else:
2025-07-01 02:59:42.553 # the synch pair is identical
2025-07-01 02:59:42.558 yield '  ' + aelt
2025-07-01 02:59:42.562
2025-07-01 02:59:42.567 # pump out diffs from after the synch point
2025-07-01 02:59:42.571 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:42.576
2025-07-01 02:59:42.580 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:42.585 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:42.589
2025-07-01 02:59:42.593 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:42.598 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:42.604 alo = 50, ahi = 1101
2025-07-01 02:59:42.611 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:42.616 blo = 50, bhi = 1101
2025-07-01 02:59:42.620
2025-07-01 02:59:42.625 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:42.629 g = []
2025-07-01 02:59:42.634 if alo < ahi:
2025-07-01 02:59:42.638 if blo < bhi:
2025-07-01 02:59:42.643 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:42.648 else:
2025-07-01 02:59:42.653 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:42.658 elif blo < bhi:
2025-07-01 02:59:42.663 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:42.667
2025-07-01 02:59:42.672 >       yield from g
2025-07-01 02:59:42.676
2025-07-01 02:59:42.681 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:42.686 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:42.691
2025-07-01 02:59:42.695 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:42.700 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:42.705 alo = 50, ahi = 1101
2025-07-01 02:59:42.710 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:42.714 blo = 50, bhi = 1101
2025-07-01 02:59:42.719
2025-07-01 02:59:42.723 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:42.728 r"""
2025-07-01 02:59:42.734 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:42.739 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:42.743 synch point, and intraline difference marking is done on the
2025-07-01 02:59:42.748 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:42.752
2025-07-01 02:59:42.756 Example:
2025-07-01 02:59:42.761
2025-07-01 02:59:42.765 >>> d = Differ()
2025-07-01 02:59:42.770 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:42.774 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:42.778 >>> print(''.join(results), end="")
2025-07-01 02:59:42.783 - abcDefghiJkl
2025-07-01 02:59:42.791 + abcdefGhijkl
2025-07-01 02:59:42.801 """
2025-07-01 02:59:42.805
2025-07-01 02:59:42.810 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:42.814 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:42.819 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:42.824 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:42.829 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:42.834
2025-07-01 02:59:42.839 # search for the pair that matches best without being identical
2025-07-01 02:59:42.844 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:42.848 # on junk -- unless we have to)
2025-07-01 02:59:42.853 for j in range(blo, bhi):
2025-07-01 02:59:42.857 bj = b[j]
2025-07-01 02:59:42.861 cruncher.set_seq2(bj)
2025-07-01 02:59:42.866 for i in range(alo, ahi):
2025-07-01 02:59:42.870 ai = a[i]
2025-07-01 02:59:42.874 if ai == bj:
2025-07-01 02:59:42.879 if eqi is None:
2025-07-01 02:59:42.883 eqi, eqj = i, j
2025-07-01 02:59:42.887 continue
2025-07-01 02:59:42.891 cruncher.set_seq1(ai)
2025-07-01 02:59:42.896 # computing similarity is expensive, so use the quick
2025-07-01 02:59:42.900 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:42.905 # compares by a factor of 3.
2025-07-01 02:59:42.909 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:42.914 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:42.918 # of the computation is cached by cruncher
2025-07-01 02:59:42.923 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:42.927 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:42.932 cruncher.ratio() > best_ratio:
2025-07-01 02:59:42.936 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:42.941 if best_ratio < cutoff:
2025-07-01 02:59:42.945 # no non-identical "pretty close" pair
2025-07-01 02:59:42.949 if eqi is None:
2025-07-01 02:59:42.954 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:42.958 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:42.963 return
2025-07-01 02:59:42.968 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:42.975 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:42.980 else:
2025-07-01 02:59:42.984 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:42.989 eqi = None
2025-07-01 02:59:42.993
2025-07-01 02:59:42.998 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:43.002 # identical
2025-07-01 02:59:43.007
2025-07-01 02:59:43.011 # pump out diffs from before the synch point
2025-07-01 02:59:43.016 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:43.020
2025-07-01 02:59:43.025 # do intraline marking on the synch pair
2025-07-01 02:59:43.029 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:43.033 if eqi is None:
2025-07-01 02:59:43.038 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:43.042 atags = btags = ""
2025-07-01 02:59:43.047 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:43.052 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:43.056 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:43.060 if tag == 'replace':
2025-07-01 02:59:43.065 atags += '^' * la
2025-07-01 02:59:43.069 btags += '^' * lb
2025-07-01 02:59:43.073 elif tag == 'delete':
2025-07-01 02:59:43.078 atags += '-' * la
2025-07-01 02:59:43.082 elif tag == 'insert':
2025-07-01 02:59:43.086 btags += '+' * lb
2025-07-01 02:59:43.091 elif tag == 'equal':
2025-07-01 02:59:43.095 atags += ' ' * la
2025-07-01 02:59:43.100 btags += ' ' * lb
2025-07-01 02:59:43.104 else:
2025-07-01 02:59:43.109 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:43.114 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:43.118 else:
2025-07-01 02:59:43.122 # the synch pair is identical
2025-07-01 02:59:43.127 yield '  ' + aelt
2025-07-01 02:59:43.131
2025-07-01 02:59:43.135 # pump out diffs from after the synch point
2025-07-01 02:59:43.139 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:43.143
2025-07-01 02:59:43.148 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:43.152 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:43.157
2025-07-01 02:59:43.161 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:43.166 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:43.170 alo = 51, ahi = 1101
2025-07-01 02:59:43.176 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:43.180 blo = 51, bhi = 1101
2025-07-01 02:59:43.185
2025-07-01 02:59:43.189 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:43.193 g = []
2025-07-01 02:59:43.199 if alo < ahi:
2025-07-01 02:59:43.204 if blo < bhi:
2025-07-01 02:59:43.209 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:43.213 else:
2025-07-01 02:59:43.218 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:43.224 elif blo < bhi:
2025-07-01 02:59:43.228 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:43.232
2025-07-01 02:59:43.236 >       yield from g
2025-07-01 02:59:43.241
2025-07-01 02:59:43.245 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:43.250 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:43.254
2025-07-01 02:59:43.258 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:43.264 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:43.268 alo = 51, ahi = 1101
2025-07-01 02:59:43.274 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:43.279 blo = 51, bhi = 1101
2025-07-01 02:59:43.283
2025-07-01 02:59:43.287 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:43.292 r"""
2025-07-01 02:59:43.297 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:43.302 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:43.307 synch point, and intraline difference marking is done on the
2025-07-01 02:59:43.311 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:43.315
2025-07-01 02:59:43.321 Example:
2025-07-01 02:59:43.326
2025-07-01 02:59:43.331 >>> d = Differ()
2025-07-01 02:59:43.335 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:43.342 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:43.348 >>> print(''.join(results), end="")
2025-07-01 02:59:43.354 - abcDefghiJkl
2025-07-01 02:59:43.369 + abcdefGhijkl
2025-07-01 02:59:43.380 """
2025-07-01 02:59:43.385
2025-07-01 02:59:43.390 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:43.395 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:43.399 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:43.404 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:43.409 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:43.413
2025-07-01 02:59:43.418 # search for the pair that matches best without being identical
2025-07-01 02:59:43.422 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:43.427 # on junk -- unless we have to)
2025-07-01 02:59:43.431 for j in range(blo, bhi):
2025-07-01 02:59:43.435 bj = b[j]
2025-07-01 02:59:43.440 cruncher.set_seq2(bj)
2025-07-01 02:59:43.444 for i in range(alo, ahi):
2025-07-01 02:59:43.449 ai = a[i]
2025-07-01 02:59:43.453 if ai == bj:
2025-07-01 02:59:43.460 if eqi is None:
2025-07-01 02:59:43.468 eqi, eqj = i, j
2025-07-01 02:59:43.476 continue
2025-07-01 02:59:43.481 cruncher.set_seq1(ai)
2025-07-01 02:59:43.488 # computing similarity is expensive, so use the quick
2025-07-01 02:59:43.493 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:43.497 # compares by a factor of 3.
2025-07-01 02:59:43.506 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:43.513 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:43.517 # of the computation is cached by cruncher
2025-07-01 02:59:43.527 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:43.537 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:43.552 cruncher.ratio() > best_ratio:
2025-07-01 02:59:43.560 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:43.571 if best_ratio < cutoff:
2025-07-01 02:59:43.581 # no non-identical "pretty close" pair
2025-07-01 02:59:43.590 if eqi is None:
2025-07-01 02:59:43.596 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:43.603 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:43.618 return
2025-07-01 02:59:43.626 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:43.645 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:43.658 else:
2025-07-01 02:59:43.666 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:43.682 eqi = None
2025-07-01 02:59:43.698
2025-07-01 02:59:43.710 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:43.722 # identical
2025-07-01 02:59:43.730
2025-07-01 02:59:43.742 # pump out diffs from before the synch point
2025-07-01 02:59:43.754 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:43.762
2025-07-01 02:59:43.770 # do intraline marking on the synch pair
2025-07-01 02:59:43.784 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:43.794 if eqi is None:
2025-07-01 02:59:43.806 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:43.818 atags = btags = ""
2025-07-01 02:59:43.826 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:43.842 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:43.850 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:43.858 if tag == 'replace':
2025-07-01 02:59:43.870 atags += '^' * la
2025-07-01 02:59:43.881 btags += '^' * lb
2025-07-01 02:59:43.896 elif tag == 'delete':
2025-07-01 02:59:43.900 atags += '-' * la
2025-07-01 02:59:43.905 elif tag == 'insert':
2025-07-01 02:59:43.909 btags += '+' * lb
2025-07-01 02:59:43.913 elif tag == 'equal':
2025-07-01 02:59:43.917 atags += ' ' * la
2025-07-01 02:59:43.922 btags += ' ' * lb
2025-07-01 02:59:43.926 else:
2025-07-01 02:59:43.930 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:43.935 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:43.939 else:
2025-07-01 02:59:43.943 # the synch pair is identical
2025-07-01 02:59:43.947 yield '  ' + aelt
2025-07-01 02:59:43.952
2025-07-01 02:59:43.956 # pump out diffs from after the synch point
2025-07-01 02:59:43.960 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:43.965
2025-07-01 02:59:43.969 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:43.974 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:43.978
2025-07-01 02:59:43.982 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:43.988 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:43.992 alo = 52, ahi = 1101
2025-07-01 02:59:43.998 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:44.002 blo = 52, bhi = 1101
2025-07-01 02:59:44.007
2025-07-01 02:59:44.011 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:44.016 g = []
2025-07-01 02:59:44.020 if alo < ahi:
2025-07-01 02:59:44.025 if blo < bhi:
2025-07-01 02:59:44.030 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:44.035 else:
2025-07-01 02:59:44.040 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:44.045 elif blo < bhi:
2025-07-01 02:59:44.049 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:44.054
2025-07-01 02:59:44.058 >       yield from g
2025-07-01 02:59:44.063
2025-07-01 02:59:44.068 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:44.072 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:44.077
2025-07-01 02:59:44.082 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:44.088 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:44.093 alo = 52, ahi = 1101
2025-07-01 02:59:44.099 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:44.104 blo = 52, bhi = 1101
2025-07-01 02:59:44.109
2025-07-01 02:59:44.115 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:44.119 r"""
2025-07-01 02:59:44.124 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:44.129 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:44.134 synch point, and intraline difference marking is done on the
2025-07-01 02:59:44.141 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:44.145
2025-07-01 02:59:44.150 Example:
2025-07-01 02:59:44.154
2025-07-01 02:59:44.159 >>> d = Differ()
2025-07-01 02:59:44.163 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:44.168 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:44.172 >>> print(''.join(results), end="")
2025-07-01 02:59:44.176 - abcDefghiJkl
2025-07-01 02:59:44.185 + abcdefGhijkl
2025-07-01 02:59:44.194 """
2025-07-01 02:59:44.198
2025-07-01 02:59:44.203 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:44.207 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:44.212 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:44.217 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:44.222 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:44.226
2025-07-01 02:59:44.232 # search for the pair that matches best without being identical
2025-07-01 02:59:44.237 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:44.241 # on junk -- unless we have to)
2025-07-01 02:59:44.246 for j in range(blo, bhi):
2025-07-01 02:59:44.250 bj = b[j]
2025-07-01 02:59:44.255 cruncher.set_seq2(bj)
2025-07-01 02:59:44.259 for i in range(alo, ahi):
2025-07-01 02:59:44.264 ai = a[i]
2025-07-01 02:59:44.268 if ai == bj:
2025-07-01 02:59:44.272 if eqi is None:
2025-07-01 02:59:44.277 eqi, eqj = i, j
2025-07-01 02:59:44.281 continue
2025-07-01 02:59:44.286 cruncher.set_seq1(ai)
2025-07-01 02:59:44.290 # computing similarity is expensive, so use the quick
2025-07-01 02:59:44.295 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:44.299 # compares by a factor of 3.
2025-07-01 02:59:44.304 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:44.309 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:44.313 # of the computation is cached by cruncher
2025-07-01 02:59:44.318 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:44.323 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:44.327 cruncher.ratio() > best_ratio:
2025-07-01 02:59:44.332 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:44.336 if best_ratio < cutoff:
2025-07-01 02:59:44.341 # no non-identical "pretty close" pair
2025-07-01 02:59:44.345 if eqi is None:
2025-07-01 02:59:44.350 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:44.354 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:44.359 return
2025-07-01 02:59:44.366 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:44.370 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:44.375 else:
2025-07-01 02:59:44.379 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:44.384 eqi = None
2025-07-01 02:59:44.388
2025-07-01 02:59:44.393 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:44.397 # identical
2025-07-01 02:59:44.402
2025-07-01 02:59:44.408 # pump out diffs from before the synch point
2025-07-01 02:59:44.414 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:44.418
2025-07-01 02:59:44.422 # do intraline marking on the synch pair
2025-07-01 02:59:44.426 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:44.431 if eqi is None:
2025-07-01 02:59:44.435 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:44.440 atags = btags = ""
2025-07-01 02:59:44.444 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:44.448 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:44.453 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:44.459 if tag == 'replace':
2025-07-01 02:59:44.463 atags += '^' * la
2025-07-01 02:59:44.467 btags += '^' * lb
2025-07-01 02:59:44.471 elif tag == 'delete':
2025-07-01 02:59:44.475 atags += '-' * la
2025-07-01 02:59:44.480 elif tag == 'insert':
2025-07-01 02:59:44.484 btags += '+' * lb
2025-07-01 02:59:44.488 elif tag == 'equal':
2025-07-01 02:59:44.492 atags += ' ' * la
2025-07-01 02:59:44.497 btags += ' ' * lb
2025-07-01 02:59:44.501 else:
2025-07-01 02:59:44.507 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:44.511 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:44.516 else:
2025-07-01 02:59:44.523 # the synch pair is identical
2025-07-01 02:59:44.530 yield '  ' + aelt
2025-07-01 02:59:44.538
2025-07-01 02:59:44.546 # pump out diffs from after the synch point
2025-07-01 02:59:44.554 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:44.562
2025-07-01 02:59:44.576 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:44.592 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:44.602
2025-07-01 02:59:44.618 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:44.628 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:44.642 alo = 53, ahi = 1101
2025-07-01 02:59:44.654 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:44.662 blo = 53, bhi = 1101
2025-07-01 02:59:44.674
2025-07-01 02:59:44.682 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:44.694 g = []
2025-07-01 02:59:44.702 if alo < ahi:
2025-07-01 02:59:44.713 if blo < bhi:
2025-07-01 02:59:44.721 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:44.732 else:
2025-07-01 02:59:44.744 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:44.762 elif blo < bhi:
2025-07-01 02:59:44.770 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:44.782
2025-07-01 02:59:44.789 >       yield from g
2025-07-01 02:59:44.802
2025-07-01 02:59:44.810 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:44.830 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:44.838
2025-07-01 02:59:44.845 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:44.858 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:44.866 alo = 53, ahi = 1101
2025-07-01 02:59:44.878 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:44.890 blo = 53, bhi = 1101
2025-07-01 02:59:44.897
2025-07-01 02:59:44.909 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:44.918 r"""
2025-07-01 02:59:44.926 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:44.941 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:44.950 synch point, and intraline difference marking is done on the
2025-07-01 02:59:44.960 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:44.974
2025-07-01 02:59:44.981 Example:
2025-07-01 02:59:44.990
2025-07-01 02:59:45.006 >>> d = Differ()
2025-07-01 02:59:45.014 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:45.030 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:45.038 >>> print(''.join(results), end="")
2025-07-01 02:59:45.050 - abcDefghiJkl
2025-07-01 02:59:45.070 + abcdefGhijkl
2025-07-01 02:59:45.094 """
2025-07-01 02:59:45.106
2025-07-01 02:59:45.114 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:45.124 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:45.134 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:45.144 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:45.162 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:45.169
2025-07-01 02:59:45.178 # search for the pair that matches best without being identical
2025-07-01 02:59:45.190 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:45.200 # on junk -- unless we have to)
2025-07-01 02:59:45.208 for j in range(blo, bhi):
2025-07-01 02:59:45.221 bj = b[j]
2025-07-01 02:59:45.234 cruncher.set_seq2(bj)
2025-07-01 02:59:45.245 for i in range(alo, ahi):
2025-07-01 02:59:45.261 ai = a[i]
2025-07-01 02:59:45.274 if ai == bj:
2025-07-01 02:59:45.285 if eqi is None:
2025-07-01 02:59:45.301 eqi, eqj = i, j
2025-07-01 02:59:45.314 continue
2025-07-01 02:59:45.325 cruncher.set_seq1(ai)
2025-07-01 02:59:45.341 # computing similarity is expensive, so use the quick
2025-07-01 02:59:45.354 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:45.364 # compares by a factor of 3.
2025-07-01 02:59:45.376 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:45.390 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:45.402 # of the computation is cached by cruncher
2025-07-01 02:59:45.414 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:45.430 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:45.442 cruncher.ratio() > best_ratio:
2025-07-01 02:59:45.454 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:45.469 if best_ratio < cutoff:
2025-07-01 02:59:45.478 # no non-identical "pretty close" pair
2025-07-01 02:59:45.490 if eqi is None:
2025-07-01 02:59:45.502 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:45.512 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:45.526 return
2025-07-01 02:59:45.538 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:45.554 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:45.561 else:
2025-07-01 02:59:45.569 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:45.582 eqi = None
2025-07-01 02:59:45.594
2025-07-01 02:59:45.606 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:45.614 # identical
2025-07-01 02:59:45.621
2025-07-01 02:59:45.634 # pump out diffs from before the synch point
2025-07-01 02:59:45.646 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:45.662
2025-07-01 02:59:45.667 # do intraline marking on the synch pair
2025-07-01 02:59:45.678 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:45.686 if eqi is None:
2025-07-01 02:59:45.694 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:45.710 atags = btags = ""
2025-07-01 02:59:45.722 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:45.734 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:45.742 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:45.754 if tag == 'replace':
2025-07-01 02:59:45.770 atags += '^' * la
2025-07-01 02:59:45.778 btags += '^' * lb
2025-07-01 02:59:45.794 elif tag == 'delete':
2025-07-01 02:59:45.802 atags += '-' * la
2025-07-01 02:59:45.814 elif tag == 'insert':
2025-07-01 02:59:45.822 btags += '+' * lb
2025-07-01 02:59:45.829 elif tag == 'equal':
2025-07-01 02:59:45.844 atags += ' ' * la
2025-07-01 02:59:45.858 btags += ' ' * lb
2025-07-01 02:59:45.866 else:
2025-07-01 02:59:45.882 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:45.890 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:45.902 else:
2025-07-01 02:59:45.913 # the synch pair is identical
2025-07-01 02:59:45.922 yield '  ' + aelt
2025-07-01 02:59:45.934
2025-07-01 02:59:45.946 # pump out diffs from after the synch point
2025-07-01 02:59:45.954 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:45.968
2025-07-01 02:59:45.978 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:45.994 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:46.002
2025-07-01 02:59:46.014 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:46.026 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:46.044 alo = 54, ahi = 1101
2025-07-01 02:59:46.060 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:46.070 blo = 54, bhi = 1101
2025-07-01 02:59:46.085
2025-07-01 02:59:46.095 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:46.103 g = []
2025-07-01 02:59:46.120 if alo < ahi:
2025-07-01 02:59:46.136 if blo < bhi:
2025-07-01 02:59:46.150 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:46.158 else:
2025-07-01 02:59:46.170 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:46.178 elif blo < bhi:
2025-07-01 02:59:46.190 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:46.198
2025-07-01 02:59:46.213 >       yield from g
2025-07-01 02:59:46.222
2025-07-01 02:59:46.237 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:46.254 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:46.266
2025-07-01 02:59:46.282 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:46.290 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:46.302 alo = 54, ahi = 1101
2025-07-01 02:59:46.314 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:46.322 blo = 54, bhi = 1101
2025-07-01 02:59:46.331
2025-07-01 02:59:46.346 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:46.354 r"""
2025-07-01 02:59:46.362 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:46.374 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:46.382 synch point, and intraline difference marking is done on the
2025-07-01 02:59:46.394 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:46.402
2025-07-01 02:59:46.414 Example:
2025-07-01 02:59:46.422
2025-07-01 02:59:46.434 >>> d = Differ()
2025-07-01 02:59:46.446 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:46.454 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:46.464 >>> print(''.join(results), end="")
2025-07-01 02:59:46.478 - abcDefghiJkl
2025-07-01 02:59:46.498 + abcdefGhijkl
2025-07-01 02:59:46.514 """
2025-07-01 02:59:46.525
2025-07-01 02:59:46.532 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:46.537 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:46.542 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:46.547 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:46.552 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:46.558
2025-07-01 02:59:46.565 # search for the pair that matches best without being identical
2025-07-01 02:59:46.572 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:46.577 # on junk -- unless we have to)
2025-07-01 02:59:46.582 for j in range(blo, bhi):
2025-07-01 02:59:46.587 bj = b[j]
2025-07-01 02:59:46.592 cruncher.set_seq2(bj)
2025-07-01 02:59:46.597 for i in range(alo, ahi):
2025-07-01 02:59:46.603 ai = a[i]
2025-07-01 02:59:46.608 if ai == bj:
2025-07-01 02:59:46.612 if eqi is None:
2025-07-01 02:59:46.618 eqi, eqj = i, j
2025-07-01 02:59:46.624 continue
2025-07-01 02:59:46.629 cruncher.set_seq1(ai)
2025-07-01 02:59:46.634 # computing similarity is expensive, so use the quick
2025-07-01 02:59:46.639 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:46.643 # compares by a factor of 3.
2025-07-01 02:59:46.648 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:46.652 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:46.658 # of the computation is cached by cruncher
2025-07-01 02:59:46.663 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:46.668 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:46.673 cruncher.ratio() > best_ratio:
2025-07-01 02:59:46.679 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:46.685 if best_ratio < cutoff:
2025-07-01 02:59:46.691 # no non-identical "pretty close" pair
2025-07-01 02:59:46.696 if eqi is None:
2025-07-01 02:59:46.701 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:46.706 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:46.711 return
2025-07-01 02:59:46.719 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:46.723 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:46.728 else:
2025-07-01 02:59:46.733 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:46.738 eqi = None
2025-07-01 02:59:46.743
2025-07-01 02:59:46.748 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:46.752 # identical
2025-07-01 02:59:46.756
2025-07-01 02:59:46.761 # pump out diffs from before the synch point
2025-07-01 02:59:46.766 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:46.771
2025-07-01 02:59:46.777 # do intraline marking on the synch pair
2025-07-01 02:59:46.781 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:46.786 if eqi is None:
2025-07-01 02:59:46.790 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:46.795 atags = btags = ""
2025-07-01 02:59:46.799 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:46.804 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:46.809 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:46.814 if tag == 'replace':
2025-07-01 02:59:46.821 atags += '^' * la
2025-07-01 02:59:46.825 btags += '^' * lb
2025-07-01 02:59:46.835 elif tag == 'delete':
2025-07-01 02:59:46.847 atags += '-' * la
2025-07-01 02:59:46.859 elif tag == 'insert':
2025-07-01 02:59:46.879 btags += '+' * lb
2025-07-01 02:59:46.899 elif tag == 'equal':
2025-07-01 02:59:46.914 atags += ' ' * la
2025-07-01 02:59:46.921 btags += ' ' * lb
2025-07-01 02:59:46.938 else:
2025-07-01 02:59:46.950 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:46.966 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:46.978 else:
2025-07-01 02:59:46.990 # the synch pair is identical
2025-07-01 02:59:46.998 yield '  ' + aelt
2025-07-01 02:59:47.014
2025-07-01 02:59:47.031 # pump out diffs from after the synch point
2025-07-01 02:59:47.046 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:47.057
2025-07-01 02:59:47.065 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:47.076 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:47.081
2025-07-01 02:59:47.085 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:47.094 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:47.099 alo = 55, ahi = 1101
2025-07-01 02:59:47.104 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:47.108 blo = 55, bhi = 1101
2025-07-01 02:59:47.113
2025-07-01 02:59:47.117 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:47.121 g = []
2025-07-01 02:59:47.126 if alo < ahi:
2025-07-01 02:59:47.130 if blo < bhi:
2025-07-01 02:59:47.135 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:47.139 else:
2025-07-01 02:59:47.144 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:47.148 elif blo < bhi:
2025-07-01 02:59:47.152 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:47.157
2025-07-01 02:59:47.161 >       yield from g
2025-07-01 02:59:47.166
2025-07-01 02:59:47.170 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:47.175 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:47.179
2025-07-01 02:59:47.184 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:47.189 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:47.193 alo = 55, ahi = 1101
2025-07-01 02:59:47.198 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:47.203 blo = 55, bhi = 1101
2025-07-01 02:59:47.207
2025-07-01 02:59:47.212 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:47.216 r"""
2025-07-01 02:59:47.220 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:47.225 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:47.230 synch point, and intraline difference marking is done on the
2025-07-01 02:59:47.234 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:47.238
2025-07-01 02:59:47.243 Example:
2025-07-01 02:59:47.247
2025-07-01 02:59:47.251 >>> d = Differ()
2025-07-01 02:59:47.256 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:47.261 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:47.265 >>> print(''.join(results), end="")
2025-07-01 02:59:47.270 - abcDefghiJkl
2025-07-01 02:59:47.279 + abcdefGhijkl
2025-07-01 02:59:47.294 """
2025-07-01 02:59:47.302
2025-07-01 02:59:47.312 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:47.322 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:47.334 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:47.342 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:47.354 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:47.364
2025-07-01 02:59:47.369 # search for the pair that matches best without being identical
2025-07-01 02:59:47.384 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:47.398 # on junk -- unless we have to)
2025-07-01 02:59:47.406 for j in range(blo, bhi):
2025-07-01 02:59:47.418 bj = b[j]
2025-07-01 02:59:47.426 cruncher.set_seq2(bj)
2025-07-01 02:59:47.442 for i in range(alo, ahi):
2025-07-01 02:59:47.450 ai = a[i]
2025-07-01 02:59:47.465 if ai == bj:
2025-07-01 02:59:47.477 if eqi is None:
2025-07-01 02:59:47.490 eqi, eqj = i, j
2025-07-01 02:59:47.503 continue
2025-07-01 02:59:47.518 cruncher.set_seq1(ai)
2025-07-01 02:59:47.530 # computing similarity is expensive, so use the quick
2025-07-01 02:59:47.544 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:47.553 # compares by a factor of 3.
2025-07-01 02:59:47.569 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:47.582 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:47.597 # of the computation is cached by cruncher
2025-07-01 02:59:47.610 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:47.625 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:47.638 cruncher.ratio() > best_ratio:
2025-07-01 02:59:47.653 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:47.666 if best_ratio < cutoff:
2025-07-01 02:59:47.677 # no non-identical "pretty close" pair
2025-07-01 02:59:47.690 if eqi is None:
2025-07-01 02:59:47.704 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:47.714 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:47.726 return
2025-07-01 02:59:47.741 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:47.754 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:47.759 else:
2025-07-01 02:59:47.776 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:47.784 eqi = None
2025-07-01 02:59:47.800
2025-07-01 02:59:47.808 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:47.824 # identical
2025-07-01 02:59:47.834
2025-07-01 02:59:47.848 # pump out diffs from before the synch point
2025-07-01 02:59:47.856 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:47.870
2025-07-01 02:59:47.881 # do intraline marking on the synch pair
2025-07-01 02:59:47.890 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:47.902 if eqi is None:
2025-07-01 02:59:47.914 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:47.928 atags = btags = ""
2025-07-01 02:59:47.942 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:47.950 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:47.964 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:47.980 if tag == 'replace':
2025-07-01 02:59:47.992 atags += '^' * la
2025-07-01 02:59:48.002 btags += '^' * lb
2025-07-01 02:59:48.022 elif tag == 'delete':
2025-07-01 02:59:48.037 atags += '-' * la
2025-07-01 02:59:48.046 elif tag == 'insert':
2025-07-01 02:59:48.055 btags += '+' * lb
2025-07-01 02:59:48.066 elif tag == 'equal':
2025-07-01 02:59:48.078 atags += ' ' * la
2025-07-01 02:59:48.094 btags += ' ' * lb
2025-07-01 02:59:48.102 else:
2025-07-01 02:59:48.118 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:48.130 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:48.144 else:
2025-07-01 02:59:48.154 # the synch pair is identical
2025-07-01 02:59:48.166 yield '  ' + aelt
2025-07-01 02:59:48.180
2025-07-01 02:59:48.194 # pump out diffs from after the synch point
2025-07-01 02:59:48.202 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:48.214
2025-07-01 02:59:48.222 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:48.232 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:48.249
2025-07-01 02:59:48.258 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:48.270 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:48.278 alo = 56, ahi = 1101
2025-07-01 02:59:48.286 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:48.294 blo = 56, bhi = 1101
2025-07-01 02:59:48.303
2025-07-01 02:59:48.314 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:48.325 g = []
2025-07-01 02:59:48.335 if alo < ahi:
2025-07-01 02:59:48.345 if blo < bhi:
2025-07-01 02:59:48.354 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:48.366 else:
2025-07-01 02:59:48.385 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:48.398 elif blo < bhi:
2025-07-01 02:59:48.409 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:48.420
2025-07-01 02:59:48.432 >       yield from g
2025-07-01 02:59:48.442
2025-07-01 02:59:48.451 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:48.468 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:48.478
2025-07-01 02:59:48.494 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:48.502 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:48.518 alo = 56, ahi = 1101
2025-07-01 02:59:48.534 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:48.541 blo = 56, bhi = 1101
2025-07-01 02:59:48.549
2025-07-01 02:59:48.561 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:48.574 r"""
2025-07-01 02:59:48.583 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:48.596 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:48.610 synch point, and intraline difference marking is done on the
2025-07-01 02:59:48.622 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:48.630
2025-07-01 02:59:48.638 Example:
2025-07-01 02:59:48.651
2025-07-01 02:59:48.666 >>> d = Differ()
2025-07-01 02:59:48.678 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:48.689 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:48.701 >>> print(''.join(results), end="")
2025-07-01 02:59:48.705 - abcDefghiJkl
2025-07-01 02:59:48.730 + abcdefGhijkl
2025-07-01 02:59:48.756 """
2025-07-01 02:59:48.766
2025-07-01 02:59:48.781 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:48.794 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:48.803 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:48.818 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:48.829 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:48.837
2025-07-01 02:59:48.854 # search for the pair that matches best without being identical
2025-07-01 02:59:48.861 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:48.870 # on junk -- unless we have to)
2025-07-01 02:59:48.886 for j in range(blo, bhi):
2025-07-01 02:59:48.897 bj = b[j]
2025-07-01 02:59:48.905 cruncher.set_seq2(bj)
2025-07-01 02:59:48.913 for i in range(alo, ahi):
2025-07-01 02:59:48.926 ai = a[i]
2025-07-01 02:59:48.933 if ai == bj:
2025-07-01 02:59:48.943 if eqi is None:
2025-07-01 02:59:48.960 eqi, eqj = i, j
2025-07-01 02:59:48.971 continue
2025-07-01 02:59:48.978 cruncher.set_seq1(ai)
2025-07-01 02:59:48.990 # computing similarity is expensive, so use the quick
2025-07-01 02:59:49.002 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:49.013 # compares by a factor of 3.
2025-07-01 02:59:49.022 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:49.038 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:49.046 # of the computation is cached by cruncher
2025-07-01 02:59:49.058 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:49.066 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:49.082 cruncher.ratio() > best_ratio:
2025-07-01 02:59:49.092 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:49.102 if best_ratio < cutoff:
2025-07-01 02:59:49.109 # no non-identical "pretty close" pair
2025-07-01 02:59:49.126 if eqi is None:
2025-07-01 02:59:49.134 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:49.155 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:49.174 return
2025-07-01 02:59:49.185 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:49.198 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:49.206 else:
2025-07-01 02:59:49.222 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:49.231 eqi = None
2025-07-01 02:59:49.246
2025-07-01 02:59:49.256 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:49.268 # identical
2025-07-01 02:59:49.278
2025-07-01 02:59:49.290 # pump out diffs from before the synch point
2025-07-01 02:59:49.297 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:49.308
2025-07-01 02:59:49.322 # do intraline marking on the synch pair
2025-07-01 02:59:49.334 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:49.346 if eqi is None:
2025-07-01 02:59:49.357 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:49.366 atags = btags = ""
2025-07-01 02:59:49.375 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:49.389 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:49.403 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:49.418 if tag == 'replace':
2025-07-01 02:59:49.434 atags += '^' * la
2025-07-01 02:59:49.445 btags += '^' * lb
2025-07-01 02:59:49.457 elif tag == 'delete':
2025-07-01 02:59:49.470 atags += '-' * la
2025-07-01 02:59:49.481 elif tag == 'insert':
2025-07-01 02:59:49.493 btags += '+' * lb
2025-07-01 02:59:49.504 elif tag == 'equal':
2025-07-01 02:59:49.520 atags += ' ' * la
2025-07-01 02:59:49.530 btags += ' ' * lb
2025-07-01 02:59:49.544 else:
2025-07-01 02:59:49.554 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:49.570 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:49.582 else:
2025-07-01 02:59:49.594 # the synch pair is identical
2025-07-01 02:59:49.602 yield '  ' + aelt
2025-07-01 02:59:49.610
2025-07-01 02:59:49.621 # pump out diffs from after the synch point
2025-07-01 02:59:49.636 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:49.654
2025-07-01 02:59:49.670 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:49.678 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:49.690
2025-07-01 02:59:49.698 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:49.714 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:49.722 alo = 57, ahi = 1101
2025-07-01 02:59:49.734 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:49.754 blo = 57, bhi = 1101
2025-07-01 02:59:49.763
2025-07-01 02:59:49.778 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:49.788 g = []
2025-07-01 02:59:49.801 if alo < ahi:
2025-07-01 02:59:49.810 if blo < bhi:
2025-07-01 02:59:49.822 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:49.834 else:
2025-07-01 02:59:49.845 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:49.857 elif blo < bhi:
2025-07-01 02:59:49.872 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:49.882
2025-07-01 02:59:49.891 >       yield from g
2025-07-01 02:59:49.900
2025-07-01 02:59:49.907 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:49.914 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:49.922
2025-07-01 02:59:49.933 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:49.942 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:49.954 alo = 57, ahi = 1101
2025-07-01 02:59:49.970 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:49.980 blo = 57, bhi = 1101
2025-07-01 02:59:49.990
2025-07-01 02:59:50.002 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:50.013 r"""
2025-07-01 02:59:50.026 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:50.043 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:50.058 synch point, and intraline difference marking is done on the
2025-07-01 02:59:50.070 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:50.082
2025-07-01 02:59:50.096 Example:
2025-07-01 02:59:50.109
2025-07-01 02:59:50.118 >>> d = Differ()
2025-07-01 02:59:50.134 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:50.158 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:50.170 >>> print(''.join(results), end="")
2025-07-01 02:59:50.178 - abcDefghiJkl
2025-07-01 02:59:50.205 + abcdefGhijkl
2025-07-01 02:59:50.230 """
2025-07-01 02:59:50.242
2025-07-01 02:59:50.254 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:50.264 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:50.279 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:50.290 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:50.302 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:50.312
2025-07-01 02:59:50.326 # search for the pair that matches best without being identical
2025-07-01 02:59:50.333 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:50.344 # on junk -- unless we have to)
2025-07-01 02:59:50.353 for j in range(blo, bhi):
2025-07-01 02:59:50.364 bj = b[j]
2025-07-01 02:59:50.373 cruncher.set_seq2(bj)
2025-07-01 02:59:50.386 for i in range(alo, ahi):
2025-07-01 02:59:50.398 ai = a[i]
2025-07-01 02:59:50.408 if ai == bj:
2025-07-01 02:59:50.422 if eqi is None:
2025-07-01 02:59:50.429 eqi, eqj = i, j
2025-07-01 02:59:50.442 continue
2025-07-01 02:59:50.458 cruncher.set_seq1(ai)
2025-07-01 02:59:50.469 # computing similarity is expensive, so use the quick
2025-07-01 02:59:50.484 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:50.493 # compares by a factor of 3.
2025-07-01 02:59:50.502 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:50.513 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:50.530 # of the computation is cached by cruncher
2025-07-01 02:59:50.545 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:50.554 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:50.570 cruncher.ratio() > best_ratio:
2025-07-01 02:59:50.578 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:50.590 if best_ratio < cutoff:
2025-07-01 02:59:50.602 # no non-identical "pretty close" pair
2025-07-01 02:59:50.610 if eqi is None:
2025-07-01 02:59:50.623 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:50.631 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:50.645 return
2025-07-01 02:59:50.654 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:50.666 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:50.678 else:
2025-07-01 02:59:50.690 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:50.702 eqi = None
2025-07-01 02:59:50.714
2025-07-01 02:59:50.726 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:50.734 # identical
2025-07-01 02:59:50.742
2025-07-01 02:59:50.754 # pump out diffs from before the synch point
2025-07-01 02:59:50.766 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:50.778
2025-07-01 02:59:50.786 # do intraline marking on the synch pair
2025-07-01 02:59:50.798 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:50.806 if eqi is None:
2025-07-01 02:59:50.818 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:50.826 atags = btags = ""
2025-07-01 02:59:50.845 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:50.858 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:50.866 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:50.874 if tag == 'replace':
2025-07-01 02:59:50.882 atags += '^' * la
2025-07-01 02:59:50.892 btags += '^' * lb
2025-07-01 02:59:50.902 elif tag == 'delete':
2025-07-01 02:59:50.914 atags += '-' * la
2025-07-01 02:59:50.922 elif tag == 'insert':
2025-07-01 02:59:50.935 btags += '+' * lb
2025-07-01 02:59:50.949 elif tag == 'equal':
2025-07-01 02:59:50.961 atags += ' ' * la
2025-07-01 02:59:50.971 btags += ' ' * lb
2025-07-01 02:59:50.985 else:
2025-07-01 02:59:50.994 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:51.010 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:51.018 else:
2025-07-01 02:59:51.026 # the synch pair is identical
2025-07-01 02:59:51.038 yield '  ' + aelt
2025-07-01 02:59:51.050
2025-07-01 02:59:51.061 # pump out diffs from after the synch point
2025-07-01 02:59:51.077 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:51.090
2025-07-01 02:59:51.101 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:51.114 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:51.123
2025-07-01 02:59:51.142 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:51.154 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:51.165 alo = 58, ahi = 1101
2025-07-01 02:59:51.178 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:51.189 blo = 58, bhi = 1101
2025-07-01 02:59:51.210
2025-07-01 02:59:51.222 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:51.230 g = []
2025-07-01 02:59:51.238 if alo < ahi:
2025-07-01 02:59:51.246 if blo < bhi:
2025-07-01 02:59:51.258 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:51.265 else:
2025-07-01 02:59:51.278 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:51.286 elif blo < bhi:
2025-07-01 02:59:51.298 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:51.306
2025-07-01 02:59:51.318 >       yield from g
2025-07-01 02:59:51.330
2025-07-01 02:59:51.342 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:51.354 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:51.366
2025-07-01 02:59:51.378 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:51.394 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:51.402 alo = 58, ahi = 1101
2025-07-01 02:59:51.418 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:51.425 blo = 58, bhi = 1101
2025-07-01 02:59:51.438
2025-07-01 02:59:51.444 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:51.461 r"""
2025-07-01 02:59:51.474 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:51.486 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:51.497 synch point, and intraline difference marking is done on the
2025-07-01 02:59:51.508 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:51.517
2025-07-01 02:59:51.528 Example:
2025-07-01 02:59:51.539
2025-07-01 02:59:51.550 >>> d = Differ()
2025-07-01 02:59:51.560 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:51.574 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:51.582 >>> print(''.join(results), end="")
2025-07-01 02:59:51.594 - abcDefghiJkl
2025-07-01 02:59:51.614 + abcdefGhijkl
2025-07-01 02:59:51.634 """
2025-07-01 02:59:51.642
2025-07-01 02:59:51.654 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:51.665 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:51.678 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:51.686 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:51.694 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:51.706
2025-07-01 02:59:51.718 # search for the pair that matches best without being identical
2025-07-01 02:59:51.731 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:51.738 # on junk -- unless we have to)
2025-07-01 02:59:51.751 for j in range(blo, bhi):
2025-07-01 02:59:51.762 bj = b[j]
2025-07-01 02:59:51.770 cruncher.set_seq2(bj)
2025-07-01 02:59:51.782 for i in range(alo, ahi):
2025-07-01 02:59:51.793 ai = a[i]
2025-07-01 02:59:51.808 if ai == bj:
2025-07-01 02:59:51.816 if eqi is None:
2025-07-01 02:59:51.821 eqi, eqj = i, j
2025-07-01 02:59:51.832 continue
2025-07-01 02:59:51.845 cruncher.set_seq1(ai)
2025-07-01 02:59:51.852 # computing similarity is expensive, so use the quick
2025-07-01 02:59:51.866 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:51.871 # compares by a factor of 3.
2025-07-01 02:59:51.889 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:51.894 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:51.898 # of the computation is cached by cruncher
2025-07-01 02:59:51.903 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:51.907 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:51.912 cruncher.ratio() > best_ratio:
2025-07-01 02:59:51.917 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:51.921 if best_ratio < cutoff:
2025-07-01 02:59:51.926 # no non-identical "pretty close" pair
2025-07-01 02:59:51.930 if eqi is None:
2025-07-01 02:59:51.935 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:51.940 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:51.946 return
2025-07-01 02:59:51.951 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:51.956 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:51.960 else:
2025-07-01 02:59:51.964 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:51.969 eqi = None
2025-07-01 02:59:51.973
2025-07-01 02:59:51.978 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:51.982 # identical
2025-07-01 02:59:51.987
2025-07-01 02:59:51.992 # pump out diffs from before the synch point
2025-07-01 02:59:51.997 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:52.002
2025-07-01 02:59:52.009 # do intraline marking on the synch pair
2025-07-01 02:59:52.016 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:52.022 if eqi is None:
2025-07-01 02:59:52.028 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:52.033 atags = btags = ""
2025-07-01 02:59:52.039 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:52.046 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:52.052 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:52.056 if tag == 'replace':
2025-07-01 02:59:52.061 atags += '^' * la
2025-07-01 02:59:52.066 btags += '^' * lb
2025-07-01 02:59:52.070 elif tag == 'delete':
2025-07-01 02:59:52.074 atags += '-' * la
2025-07-01 02:59:52.079 elif tag == 'insert':
2025-07-01 02:59:52.083 btags += '+' * lb
2025-07-01 02:59:52.089 elif tag == 'equal':
2025-07-01 02:59:52.093 atags += ' ' * la
2025-07-01 02:59:52.098 btags += ' ' * lb
2025-07-01 02:59:52.102 else:
2025-07-01 02:59:52.107 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:52.111 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:52.116 else:
2025-07-01 02:59:52.120 # the synch pair is identical
2025-07-01 02:59:52.124 yield '  ' + aelt
2025-07-01 02:59:52.129
2025-07-01 02:59:52.134 # pump out diffs from after the synch point
2025-07-01 02:59:52.139 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:52.143
2025-07-01 02:59:52.147 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:52.152 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:52.157
2025-07-01 02:59:52.161 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:52.166 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:52.170 alo = 59, ahi = 1101
2025-07-01 02:59:52.175 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:52.181 blo = 59, bhi = 1101
2025-07-01 02:59:52.190
2025-07-01 02:59:52.198 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:52.205 g = []
2025-07-01 02:59:52.214 if alo < ahi:
2025-07-01 02:59:52.223 if blo < bhi:
2025-07-01 02:59:52.237 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:52.250 else:
2025-07-01 02:59:52.262 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:52.274 elif blo < bhi:
2025-07-01 02:59:52.286 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:52.298
2025-07-01 02:59:52.305 >       yield from g
2025-07-01 02:59:52.314
2025-07-01 02:59:52.329 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:52.342 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:52.354
2025-07-01 02:59:52.366 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:52.373 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:52.390 alo = 59, ahi = 1101
2025-07-01 02:59:52.406 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:52.414 blo = 59, bhi = 1101
2025-07-01 02:59:52.426
2025-07-01 02:59:52.442 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:52.454 r"""
2025-07-01 02:59:52.462 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:52.474 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:52.481 synch point, and intraline difference marking is done on the
2025-07-01 02:59:52.492 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:52.502
2025-07-01 02:59:52.510 Example:
2025-07-01 02:59:52.521
2025-07-01 02:59:52.537 >>> d = Differ()
2025-07-01 02:59:52.550 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:52.562 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:52.574 >>> print(''.join(results), end="")
2025-07-01 02:59:52.582 - abcDefghiJkl
2025-07-01 02:59:52.602 + abcdefGhijkl
2025-07-01 02:59:52.622 """
2025-07-01 02:59:52.634
2025-07-01 02:59:52.650 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:52.662 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:52.669 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:52.682 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:52.696 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:52.710
2025-07-01 02:59:52.718 # search for the pair that matches best without being identical
2025-07-01 02:59:52.735 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:52.749 # on junk -- unless we have to)
2025-07-01 02:59:52.761 for j in range(blo, bhi):
2025-07-01 02:59:52.770 bj = b[j]
2025-07-01 02:59:52.786 cruncher.set_seq2(bj)
2025-07-01 02:59:52.798 for i in range(alo, ahi):
2025-07-01 02:59:52.810 ai = a[i]
2025-07-01 02:59:52.818 if ai == bj:
2025-07-01 02:59:52.826 if eqi is None:
2025-07-01 02:59:52.838 eqi, eqj = i, j
2025-07-01 02:59:52.850 continue
2025-07-01 02:59:52.857 cruncher.set_seq1(ai)
2025-07-01 02:59:52.870 # computing similarity is expensive, so use the quick
2025-07-01 02:59:52.881 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:52.889 # compares by a factor of 3.
2025-07-01 02:59:52.900 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:52.910 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:52.925 # of the computation is cached by cruncher
2025-07-01 02:59:52.938 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:52.950 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:52.962 cruncher.ratio() > best_ratio:
2025-07-01 02:59:52.978 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:52.986 if best_ratio < cutoff:
2025-07-01 02:59:53.002 # no non-identical "pretty close" pair
2025-07-01 02:59:53.014 if eqi is None:
2025-07-01 02:59:53.030 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:53.045 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:53.058 return
2025-07-01 02:59:53.067 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:53.081 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:53.094 else:
2025-07-01 02:59:53.106 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:53.116 eqi = None
2025-07-01 02:59:53.132
2025-07-01 02:59:53.142 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:53.150 # identical
2025-07-01 02:59:53.158
2025-07-01 02:59:53.174 # pump out diffs from before the synch point
2025-07-01 02:59:53.184 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:53.189
2025-07-01 02:59:53.198 # do intraline marking on the synch pair
2025-07-01 02:59:53.212 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:53.222 if eqi is None:
2025-07-01 02:59:53.229 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:53.238 atags = btags = ""
2025-07-01 02:59:53.247 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:53.258 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:53.270 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:53.287 if tag == 'replace':
2025-07-01 02:59:53.297 atags += '^' * la
2025-07-01 02:59:53.305 btags += '^' * lb
2025-07-01 02:59:53.317 elif tag == 'delete':
2025-07-01 02:59:53.322 atags += '-' * la
2025-07-01 02:59:53.327 elif tag == 'insert':
2025-07-01 02:59:53.333 btags += '+' * lb
2025-07-01 02:59:53.338 elif tag == 'equal':
2025-07-01 02:59:53.343 atags += ' ' * la
2025-07-01 02:59:53.355 btags += ' ' * lb
2025-07-01 02:59:53.374 else:
2025-07-01 02:59:53.382 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:53.394 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:53.399 else:
2025-07-01 02:59:53.416 # the synch pair is identical
2025-07-01 02:59:53.430 yield '  ' + aelt
2025-07-01 02:59:53.442
2025-07-01 02:59:53.454 # pump out diffs from after the synch point
2025-07-01 02:59:53.462 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:53.474
2025-07-01 02:59:53.482 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:53.490 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:53.497
2025-07-01 02:59:53.510 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:53.519 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:53.534 alo = 60, ahi = 1101
2025-07-01 02:59:53.544 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:53.554 blo = 60, bhi = 1101
2025-07-01 02:59:53.566
2025-07-01 02:59:53.574 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:53.586 g = []
2025-07-01 02:59:53.594 if alo < ahi:
2025-07-01 02:59:53.600 if blo < bhi:
2025-07-01 02:59:53.605 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:53.609 else:
2025-07-01 02:59:53.614 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:53.619 elif blo < bhi:
2025-07-01 02:59:53.624 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:53.628
2025-07-01 02:59:53.633 >       yield from g
2025-07-01 02:59:53.637
2025-07-01 02:59:53.642 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:53.648 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:53.654
2025-07-01 02:59:53.658 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:53.664 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:53.668 alo = 60, ahi = 1101
2025-07-01 02:59:53.675 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:53.680 blo = 60, bhi = 1101
2025-07-01 02:59:53.685
2025-07-01 02:59:53.690 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:53.694 r"""
2025-07-01 02:59:53.698 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:53.702 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:53.707 synch point, and intraline difference marking is done on the
2025-07-01 02:59:53.712 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:53.716
2025-07-01 02:59:53.720 Example:
2025-07-01 02:59:53.725
2025-07-01 02:59:53.729 >>> d = Differ()
2025-07-01 02:59:53.734 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:53.738 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:53.743 >>> print(''.join(results), end="")
2025-07-01 02:59:53.747 - abcDefghiJkl
2025-07-01 02:59:53.756 + abcdefGhijkl
2025-07-01 02:59:53.764 """
2025-07-01 02:59:53.769
2025-07-01 02:59:53.774 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:53.778 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:53.783 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:53.787 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:53.792 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:53.796
2025-07-01 02:59:53.801 # search for the pair that matches best without being identical
2025-07-01 02:59:53.805 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:53.810 # on junk -- unless we have to)
2025-07-01 02:59:53.814 for j in range(blo, bhi):
2025-07-01 02:59:53.818 bj = b[j]
2025-07-01 02:59:53.823 cruncher.set_seq2(bj)
2025-07-01 02:59:53.827 for i in range(alo, ahi):
2025-07-01 02:59:53.832 ai = a[i]
2025-07-01 02:59:53.837 if ai == bj:
2025-07-01 02:59:53.842 if eqi is None:
2025-07-01 02:59:53.847 eqi, eqj = i, j
2025-07-01 02:59:53.851 continue
2025-07-01 02:59:53.856 cruncher.set_seq1(ai)
2025-07-01 02:59:53.861 # computing similarity is expensive, so use the quick
2025-07-01 02:59:53.866 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:53.870 # compares by a factor of 3.
2025-07-01 02:59:53.875 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:53.880 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:53.885 # of the computation is cached by cruncher
2025-07-01 02:59:53.890 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:53.894 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:53.899 cruncher.ratio() > best_ratio:
2025-07-01 02:59:53.904 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:53.909 if best_ratio < cutoff:
2025-07-01 02:59:53.913 # no non-identical "pretty close" pair
2025-07-01 02:59:53.918 if eqi is None:
2025-07-01 02:59:53.922 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:53.927 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:53.932 return
2025-07-01 02:59:53.937 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:53.942 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:53.947 else:
2025-07-01 02:59:53.951 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:53.956 eqi = None
2025-07-01 02:59:53.960
2025-07-01 02:59:53.965 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:53.971 # identical
2025-07-01 02:59:53.976
2025-07-01 02:59:53.981 # pump out diffs from before the synch point
2025-07-01 02:59:53.986 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:53.990
2025-07-01 02:59:53.995 # do intraline marking on the synch pair
2025-07-01 02:59:53.999 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:54.004 if eqi is None:
2025-07-01 02:59:54.009 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:54.013 atags = btags = ""
2025-07-01 02:59:54.018 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:54.023 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:54.028 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:54.032 if tag == 'replace':
2025-07-01 02:59:54.037 atags += '^' * la
2025-07-01 02:59:54.041 btags += '^' * lb
2025-07-01 02:59:54.046 elif tag == 'delete':
2025-07-01 02:59:54.050 atags += '-' * la
2025-07-01 02:59:54.054 elif tag == 'insert':
2025-07-01 02:59:54.059 btags += '+' * lb
2025-07-01 02:59:54.064 elif tag == 'equal':
2025-07-01 02:59:54.068 atags += ' ' * la
2025-07-01 02:59:54.072 btags += ' ' * lb
2025-07-01 02:59:54.077 else:
2025-07-01 02:59:54.081 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:54.086 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:54.091 else:
2025-07-01 02:59:54.095 # the synch pair is identical
2025-07-01 02:59:54.100 yield '  ' + aelt
2025-07-01 02:59:54.104
2025-07-01 02:59:54.109 # pump out diffs from after the synch point
2025-07-01 02:59:54.114 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:54.119
2025-07-01 02:59:54.123 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:54.129 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:54.135
2025-07-01 02:59:54.139 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:54.146 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:54.151 alo = 61, ahi = 1101
2025-07-01 02:59:54.156 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:54.160 blo = 61, bhi = 1101
2025-07-01 02:59:54.165
2025-07-01 02:59:54.169 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:54.173 g = []
2025-07-01 02:59:54.178 if alo < ahi:
2025-07-01 02:59:54.182 if blo < bhi:
2025-07-01 02:59:54.187 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:54.192 else:
2025-07-01 02:59:54.196 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:54.201 elif blo < bhi:
2025-07-01 02:59:54.205 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:54.210
2025-07-01 02:59:54.214 >       yield from g
2025-07-01 02:59:54.219
2025-07-01 02:59:54.223 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:54.228 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:54.232
2025-07-01 02:59:54.237 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:54.242 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:54.246 alo = 61, ahi = 1101
2025-07-01 02:59:54.251 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:54.255 blo = 61, bhi = 1101
2025-07-01 02:59:54.260
2025-07-01 02:59:54.265 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:54.269 r"""
2025-07-01 02:59:54.274 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:54.279 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:54.283 synch point, and intraline difference marking is done on the
2025-07-01 02:59:54.288 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:54.292
2025-07-01 02:59:54.297 Example:
2025-07-01 02:59:54.302
2025-07-01 02:59:54.308 >>> d = Differ()
2025-07-01 02:59:54.314 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:54.319 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:54.325 >>> print(''.join(results), end="")
2025-07-01 02:59:54.331 - abcDefghiJkl
2025-07-01 02:59:54.343 + abcdefGhijkl
2025-07-01 02:59:54.354 """
2025-07-01 02:59:54.360
2025-07-01 02:59:54.366 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:54.371 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:54.377 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:54.383 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:54.389 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:54.395
2025-07-01 02:59:54.400 # search for the pair that matches best without being identical
2025-07-01 02:59:54.406 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:54.412 # on junk -- unless we have to)
2025-07-01 02:59:54.417 for j in range(blo, bhi):
2025-07-01 02:59:54.421 bj = b[j]
2025-07-01 02:59:54.425 cruncher.set_seq2(bj)
2025-07-01 02:59:54.430 for i in range(alo, ahi):
2025-07-01 02:59:54.434 ai = a[i]
2025-07-01 02:59:54.438 if ai == bj:
2025-07-01 02:59:54.443 if eqi is None:
2025-07-01 02:59:54.447 eqi, eqj = i, j
2025-07-01 02:59:54.451 continue
2025-07-01 02:59:54.462 cruncher.set_seq1(ai)
2025-07-01 02:59:54.473 # computing similarity is expensive, so use the quick
2025-07-01 02:59:54.486 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:54.493 # compares by a factor of 3.
2025-07-01 02:59:54.510 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:54.518 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:54.530 # of the computation is cached by cruncher
2025-07-01 02:59:54.538 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:54.546 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:54.562 cruncher.ratio() > best_ratio:
2025-07-01 02:59:54.574 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:54.582 if best_ratio < cutoff:
2025-07-01 02:59:54.594 # no non-identical "pretty close" pair
2025-07-01 02:59:54.602 if eqi is None:
2025-07-01 02:59:54.610 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:54.623 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:54.629 return
2025-07-01 02:59:54.637 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:54.654 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:54.661 else:
2025-07-01 02:59:54.674 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:54.686 eqi = None
2025-07-01 02:59:54.693
2025-07-01 02:59:54.710 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:54.717 # identical
2025-07-01 02:59:54.730
2025-07-01 02:59:54.742 # pump out diffs from before the synch point
2025-07-01 02:59:54.749 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:54.757
2025-07-01 02:59:54.768 # do intraline marking on the synch pair
2025-07-01 02:59:54.779 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:54.789 if eqi is None:
2025-07-01 02:59:54.802 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:54.810 atags = btags = ""
2025-07-01 02:59:54.816 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:54.826 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:54.838 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:54.849 if tag == 'replace':
2025-07-01 02:59:54.861 atags += '^' * la
2025-07-01 02:59:54.874 btags += '^' * lb
2025-07-01 02:59:54.885 elif tag == 'delete':
2025-07-01 02:59:54.899 atags += '-' * la
2025-07-01 02:59:54.914 elif tag == 'insert':
2025-07-01 02:59:54.930 btags += '+' * lb
2025-07-01 02:59:54.942 elif tag == 'equal':
2025-07-01 02:59:54.954 atags += ' ' * la
2025-07-01 02:59:54.961 btags += ' ' * lb
2025-07-01 02:59:54.974 else:
2025-07-01 02:59:54.981 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:54.990 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:55.001 else:
2025-07-01 02:59:55.010 # the synch pair is identical
2025-07-01 02:59:55.018 yield '  ' + aelt
2025-07-01 02:59:55.030
2025-07-01 02:59:55.042 # pump out diffs from after the synch point
2025-07-01 02:59:55.050 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:55.062
2025-07-01 02:59:55.070 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:55.084 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:55.098
2025-07-01 02:59:55.110 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:55.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:55.130 alo = 62, ahi = 1101
2025-07-01 02:59:55.146 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:55.158 blo = 62, bhi = 1101
2025-07-01 02:59:55.171
2025-07-01 02:59:55.184 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:55.198 g = []
2025-07-01 02:59:55.210 if alo < ahi:
2025-07-01 02:59:55.217 if blo < bhi:
2025-07-01 02:59:55.230 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:55.237 else:
2025-07-01 02:59:55.245 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:55.257 elif blo < bhi:
2025-07-01 02:59:55.265 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:55.278
2025-07-01 02:59:55.286 >       yield from g
2025-07-01 02:59:55.298
2025-07-01 02:59:55.310 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:55.322 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:55.334
2025-07-01 02:59:55.346 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:55.356 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:55.370 alo = 62, ahi = 1101
2025-07-01 02:59:55.378 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:55.394 blo = 62, bhi = 1101
2025-07-01 02:59:55.406
2025-07-01 02:59:55.418 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:55.424 r"""
2025-07-01 02:59:55.434 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:55.450 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:55.457 synch point, and intraline difference marking is done on the
2025-07-01 02:59:55.470 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:55.486
2025-07-01 02:59:55.494 Example:
2025-07-01 02:59:55.506
2025-07-01 02:59:55.510 >>> d = Differ()
2025-07-01 02:59:55.515 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:55.520 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:55.524 >>> print(''.join(results), end="")
2025-07-01 02:59:55.529 - abcDefghiJkl
2025-07-01 02:59:55.538 + abcdefGhijkl
2025-07-01 02:59:55.547 """
2025-07-01 02:59:55.551
2025-07-01 02:59:55.556 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:55.561 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:55.565 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:55.570 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:55.575 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:55.579
2025-07-01 02:59:55.584 # search for the pair that matches best without being identical
2025-07-01 02:59:55.590 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:55.598 # on junk -- unless we have to)
2025-07-01 02:59:55.607 for j in range(blo, bhi):
2025-07-01 02:59:55.616 bj = b[j]
2025-07-01 02:59:55.626 cruncher.set_seq2(bj)
2025-07-01 02:59:55.632 for i in range(alo, ahi):
2025-07-01 02:59:55.647 ai = a[i]
2025-07-01 02:59:55.652 if ai == bj:
2025-07-01 02:59:55.662 if eqi is None:
2025-07-01 02:59:55.674 eqi, eqj = i, j
2025-07-01 02:59:55.686 continue
2025-07-01 02:59:55.694 cruncher.set_seq1(ai)
2025-07-01 02:59:55.706 # computing similarity is expensive, so use the quick
2025-07-01 02:59:55.713 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:55.726 # compares by a factor of 3.
2025-07-01 02:59:55.734 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:55.746 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:55.756 # of the computation is cached by cruncher
2025-07-01 02:59:55.770 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:55.779 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:55.794 cruncher.ratio() > best_ratio:
2025-07-01 02:59:55.802 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:55.814 if best_ratio < cutoff:
2025-07-01 02:59:55.824 # no non-identical "pretty close" pair
2025-07-01 02:59:55.839 if eqi is None:
2025-07-01 02:59:55.857 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:55.866 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:55.877 return
2025-07-01 02:59:55.890 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:55.904 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:55.916 else:
2025-07-01 02:59:55.928 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:55.940 eqi = None
2025-07-01 02:59:55.951
2025-07-01 02:59:55.962 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:55.974 # identical
2025-07-01 02:59:55.986
2025-07-01 02:59:55.998 # pump out diffs from before the synch point
2025-07-01 02:59:56.004 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:56.016
2025-07-01 02:59:56.032 # do intraline marking on the synch pair
2025-07-01 02:59:56.045 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:56.053 if eqi is None:
2025-07-01 02:59:56.062 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:56.070 atags = btags = ""
2025-07-01 02:59:56.082 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:56.098 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:56.111 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:56.116 if tag == 'replace':
2025-07-01 02:59:56.129 atags += '^' * la
2025-07-01 02:59:56.145 btags += '^' * lb
2025-07-01 02:59:56.153 elif tag == 'delete':
2025-07-01 02:59:56.166 atags += '-' * la
2025-07-01 02:59:56.174 elif tag == 'insert':
2025-07-01 02:59:56.186 btags += '+' * lb
2025-07-01 02:59:56.194 elif tag == 'equal':
2025-07-01 02:59:56.202 atags += ' ' * la
2025-07-01 02:59:56.214 btags += ' ' * lb
2025-07-01 02:59:56.226 else:
2025-07-01 02:59:56.232 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:56.253 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:56.268 else:
2025-07-01 02:59:56.282 # the synch pair is identical
2025-07-01 02:59:56.290 yield '  ' + aelt
2025-07-01 02:59:56.302
2025-07-01 02:59:56.310 # pump out diffs from after the synch point
2025-07-01 02:59:56.322 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:56.330
2025-07-01 02:59:56.338 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:56.346 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:56.351
2025-07-01 02:59:56.357 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:56.363 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:56.369 alo = 63, ahi = 1101
2025-07-01 02:59:56.375 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:56.380 blo = 63, bhi = 1101
2025-07-01 02:59:56.386
2025-07-01 02:59:56.397 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:56.404 g = []
2025-07-01 02:59:56.408 if alo < ahi:
2025-07-01 02:59:56.414 if blo < bhi:
2025-07-01 02:59:56.424 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:56.429 else:
2025-07-01 02:59:56.440 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:56.450 elif blo < bhi:
2025-07-01 02:59:56.462 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:56.474
2025-07-01 02:59:56.486 >       yield from g
2025-07-01 02:59:56.497
2025-07-01 02:59:56.510 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:56.519 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:56.534
2025-07-01 02:59:56.553 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:56.566 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:56.578 alo = 63, ahi = 1101
2025-07-01 02:59:56.594 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:56.605 blo = 63, bhi = 1101
2025-07-01 02:59:56.618
2025-07-01 02:59:56.630 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:56.642 r"""
2025-07-01 02:59:56.654 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:56.674 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:56.690 synch point, and intraline difference marking is done on the
2025-07-01 02:59:56.701 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:56.714
2025-07-01 02:59:56.726 Example:
2025-07-01 02:59:56.738
2025-07-01 02:59:56.746 >>> d = Differ()
2025-07-01 02:59:56.757 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:56.770 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:56.782 >>> print(''.join(results), end="")
2025-07-01 02:59:56.790 - abcDefghiJkl
2025-07-01 02:59:56.810 + abcdefGhijkl
2025-07-01 02:59:56.834 """
2025-07-01 02:59:56.850
2025-07-01 02:59:56.857 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:56.870 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:56.877 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:56.885 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:56.904 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:56.910
2025-07-01 02:59:56.922 # search for the pair that matches best without being identical
2025-07-01 02:59:56.933 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:56.941 # on junk -- unless we have to)
2025-07-01 02:59:56.946 for j in range(blo, bhi):
2025-07-01 02:59:56.954 bj = b[j]
2025-07-01 02:59:56.970 cruncher.set_seq2(bj)
2025-07-01 02:59:56.978 for i in range(alo, ahi):
2025-07-01 02:59:56.990 ai = a[i]
2025-07-01 02:59:56.998 if ai == bj:
2025-07-01 02:59:57.010 if eqi is None:
2025-07-01 02:59:57.019 eqi, eqj = i, j
2025-07-01 02:59:57.033 continue
2025-07-01 02:59:57.041 cruncher.set_seq1(ai)
2025-07-01 02:59:57.050 # computing similarity is expensive, so use the quick
2025-07-01 02:59:57.061 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:57.074 # compares by a factor of 3.
2025-07-01 02:59:57.086 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:57.093 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:57.106 # of the computation is cached by cruncher
2025-07-01 02:59:57.113 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:57.126 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:57.138 cruncher.ratio() > best_ratio:
2025-07-01 02:59:57.146 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:57.157 if best_ratio < cutoff:
2025-07-01 02:59:57.166 # no non-identical "pretty close" pair
2025-07-01 02:59:57.178 if eqi is None:
2025-07-01 02:59:57.186 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:57.200 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:57.208 return
2025-07-01 02:59:57.222 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:57.234 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:57.246 else:
2025-07-01 02:59:57.257 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:57.266 eqi = None
2025-07-01 02:59:57.273
2025-07-01 02:59:57.288 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:57.300 # identical
2025-07-01 02:59:57.314
2025-07-01 02:59:57.321 # pump out diffs from before the synch point
2025-07-01 02:59:57.329 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:57.345
2025-07-01 02:59:57.354 # do intraline marking on the synch pair
2025-07-01 02:59:57.366 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:57.374 if eqi is None:
2025-07-01 02:59:57.386 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:57.398 atags = btags = ""
2025-07-01 02:59:57.406 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:57.418 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:57.430 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:57.440 if tag == 'replace':
2025-07-01 02:59:57.454 atags += '^' * la
2025-07-01 02:59:57.466 btags += '^' * lb
2025-07-01 02:59:57.478 elif tag == 'delete':
2025-07-01 02:59:57.490 atags += '-' * la
2025-07-01 02:59:57.502 elif tag == 'insert':
2025-07-01 02:59:57.514 btags += '+' * lb
2025-07-01 02:59:57.526 elif tag == 'equal':
2025-07-01 02:59:57.534 atags += ' ' * la
2025-07-01 02:59:57.546 btags += ' ' * lb
2025-07-01 02:59:57.553 else:
2025-07-01 02:59:57.568 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:57.585 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:57.591 else:
2025-07-01 02:59:57.609 # the synch pair is identical
2025-07-01 02:59:57.625 yield '  ' + aelt
2025-07-01 02:59:57.637
2025-07-01 02:59:57.654 # pump out diffs from after the synch point
2025-07-01 02:59:57.663 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:57.686
2025-07-01 02:59:57.694 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:57.702 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:57.714
2025-07-01 02:59:57.721 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:57.734 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:57.751 alo = 64, ahi = 1101
2025-07-01 02:59:57.767 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:57.782 blo = 64, bhi = 1101
2025-07-01 02:59:57.790
2025-07-01 02:59:57.797 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:57.805 g = []
2025-07-01 02:59:57.818 if alo < ahi:
2025-07-01 02:59:57.825 if blo < bhi:
2025-07-01 02:59:57.838 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:57.850 else:
2025-07-01 02:59:57.858 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:57.870 elif blo < bhi:
2025-07-01 02:59:57.882 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:57.894
2025-07-01 02:59:57.902 >       yield from g
2025-07-01 02:59:57.913
2025-07-01 02:59:57.928 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:57.938 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:57.949
2025-07-01 02:59:57.961 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:57.974 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:57.982 alo = 64, ahi = 1101
2025-07-01 02:59:58.002 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:58.015 blo = 64, bhi = 1101
2025-07-01 02:59:58.029
2025-07-01 02:59:58.042 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:58.054 r"""
2025-07-01 02:59:58.061 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:58.074 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:58.088 synch point, and intraline difference marking is done on the
2025-07-01 02:59:58.101 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:58.114
2025-07-01 02:59:58.126 Example:
2025-07-01 02:59:58.138
2025-07-01 02:59:58.150 >>> d = Differ()
2025-07-01 02:59:58.158 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:58.170 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:58.178 >>> print(''.join(results), end="")
2025-07-01 02:59:58.186 - abcDefghiJkl
2025-07-01 02:59:58.206 + abcdefGhijkl
2025-07-01 02:59:58.230 """
2025-07-01 02:59:58.242
2025-07-01 02:59:58.254 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:58.270 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:58.282 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:58.294 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:58.301 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:58.314
2025-07-01 02:59:58.326 # search for the pair that matches best without being identical
2025-07-01 02:59:58.335 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:58.346 # on junk -- unless we have to)
2025-07-01 02:59:58.354 for j in range(blo, bhi):
2025-07-01 02:59:58.366 bj = b[j]
2025-07-01 02:59:58.378 cruncher.set_seq2(bj)
2025-07-01 02:59:58.391 for i in range(alo, ahi):
2025-07-01 02:59:58.406 ai = a[i]
2025-07-01 02:59:58.417 if ai == bj:
2025-07-01 02:59:58.429 if eqi is None:
2025-07-01 02:59:58.440 eqi, eqj = i, j
2025-07-01 02:59:58.450 continue
2025-07-01 02:59:58.461 cruncher.set_seq1(ai)
2025-07-01 02:59:58.476 # computing similarity is expensive, so use the quick
2025-07-01 02:59:58.486 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:58.498 # compares by a factor of 3.
2025-07-01 02:59:58.506 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:58.518 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:58.530 # of the computation is cached by cruncher
2025-07-01 02:59:58.538 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:58.547 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:58.559 cruncher.ratio() > best_ratio:
2025-07-01 02:59:58.570 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:58.581 if best_ratio < cutoff:
2025-07-01 02:59:58.593 # no non-identical "pretty close" pair
2025-07-01 02:59:58.604 if eqi is None:
2025-07-01 02:59:58.614 # no identical pair either -- treat it as a straight replace
2025-07-01 02:59:58.627 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:58.634 return
2025-07-01 02:59:58.642 # no close pair, but an identical pair -- synch up on that
2025-07-01 02:59:58.654 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 02:59:58.669 else:
2025-07-01 02:59:58.679 # there's a close pair, so forget the identical pair (if any)
2025-07-01 02:59:58.690 eqi = None
2025-07-01 02:59:58.706
2025-07-01 02:59:58.714 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 02:59:58.722 # identical
2025-07-01 02:59:58.742
2025-07-01 02:59:58.753 # pump out diffs from before the synch point
2025-07-01 02:59:58.762 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 02:59:58.774
2025-07-01 02:59:58.786 # do intraline marking on the synch pair
2025-07-01 02:59:58.798 aelt, belt = a[best_i], b[best_j]
2025-07-01 02:59:58.806 if eqi is None:
2025-07-01 02:59:58.818 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 02:59:58.825 atags = btags = ""
2025-07-01 02:59:58.838 cruncher.set_seqs(aelt, belt)
2025-07-01 02:59:58.845 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 02:59:58.862 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 02:59:58.874 if tag == 'replace':
2025-07-01 02:59:58.881 atags += '^' * la
2025-07-01 02:59:58.889 btags += '^' * lb
2025-07-01 02:59:58.902 elif tag == 'delete':
2025-07-01 02:59:58.909 atags += '-' * la
2025-07-01 02:59:58.921 elif tag == 'insert':
2025-07-01 02:59:58.930 btags += '+' * lb
2025-07-01 02:59:58.942 elif tag == 'equal':
2025-07-01 02:59:58.950 atags += ' ' * la
2025-07-01 02:59:58.962 btags += ' ' * lb
2025-07-01 02:59:58.970 else:
2025-07-01 02:59:58.986 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 02:59:58.994 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 02:59:59.006 else:
2025-07-01 02:59:59.014 # the synch pair is identical
2025-07-01 02:59:59.030 yield '  ' + aelt
2025-07-01 02:59:59.038
2025-07-01 02:59:59.050 # pump out diffs from after the synch point
2025-07-01 02:59:59.058 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 02:59:59.070
2025-07-01 02:59:59.078 /usr/lib/python3.11/difflib.py:985:
2025-07-01 02:59:59.094 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:59.102
2025-07-01 02:59:59.122 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:59.130 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:59.138 alo = 65, ahi = 1101
2025-07-01 02:59:59.154 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:59.163 blo = 65, bhi = 1101
2025-07-01 02:59:59.170
2025-07-01 02:59:59.182 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:59.190 g = []
2025-07-01 02:59:59.202 if alo < ahi:
2025-07-01 02:59:59.209 if blo < bhi:
2025-07-01 02:59:59.221 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 02:59:59.227 else:
2025-07-01 02:59:59.233 g = self._dump('-', a, alo, ahi)
2025-07-01 02:59:59.247 elif blo < bhi:
2025-07-01 02:59:59.269 g = self._dump('+', b, blo, bhi)
2025-07-01 02:59:59.282
2025-07-01 02:59:59.288 >       yield from g
2025-07-01 02:59:59.309
2025-07-01 02:59:59.322 /usr/lib/python3.11/difflib.py:997:
2025-07-01 02:59:59.338 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 02:59:59.346
2025-07-01 02:59:59.353 self = <difflib.Differ object at [hex]>
2025-07-01 02:59:59.366 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 02:59:59.374 alo = 65, ahi = 1101
2025-07-01 02:59:59.386 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 02:59:59.398 blo = 65, bhi = 1101
2025-07-01 02:59:59.405
2025-07-01 02:59:59.418 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 02:59:59.426 r"""
2025-07-01 02:59:59.438 When replacing one block of lines with another, search the blocks
2025-07-01 02:59:59.445 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 02:59:59.458 synch point, and intraline difference marking is done on the
2025-07-01 02:59:59.469 similar pair. Lots of work, but often worth it.
2025-07-01 02:59:59.482
2025-07-01 02:59:59.494 Example:
2025-07-01 02:59:59.506
2025-07-01 02:59:59.514 >>> d = Differ()
2025-07-01 02:59:59.526 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 02:59:59.534 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 02:59:59.550 >>> print(''.join(results), end="")
2025-07-01 02:59:59.558 - abcDefghiJkl
2025-07-01 02:59:59.586 + abcdefGhijkl
2025-07-01 02:59:59.606 """
2025-07-01 02:59:59.616
2025-07-01 02:59:59.630 # don't synch up unless the lines have a similarity score of at
2025-07-01 02:59:59.642 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 02:59:59.650 best_ratio, cutoff = 0.74, 0.75
2025-07-01 02:59:59.662 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 02:59:59.674 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 02:59:59.686
2025-07-01 02:59:59.698 # search for the pair that matches best without being identical
2025-07-01 02:59:59.705 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 02:59:59.716 # on junk -- unless we have to)
2025-07-01 02:59:59.725 for j in range(blo, bhi):
2025-07-01 02:59:59.742 bj = b[j]
2025-07-01 02:59:59.750 cruncher.set_seq2(bj)
2025-07-01 02:59:59.765 for i in range(alo, ahi):
2025-07-01 02:59:59.778 ai = a[i]
2025-07-01 02:59:59.794 if ai == bj:
2025-07-01 02:59:59.802 if eqi is None:
2025-07-01 02:59:59.814 eqi, eqj = i, j
2025-07-01 02:59:59.822 continue
2025-07-01 02:59:59.834 cruncher.set_seq1(ai)
2025-07-01 02:59:59.843 # computing similarity is expensive, so use the quick
2025-07-01 02:59:59.856 # upper bounds first -- have seen this speed up messy
2025-07-01 02:59:59.868 # compares by a factor of 3.
2025-07-01 02:59:59.882 # note that ratio() is only expensive to compute the first
2025-07-01 02:59:59.898 # time it's called on a sequence pair; the expensive part
2025-07-01 02:59:59.906 # of the computation is cached by cruncher
2025-07-01 02:59:59.918 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 02:59:59.930 cruncher.quick_ratio() > best_ratio and \
2025-07-01 02:59:59.938 cruncher.ratio() > best_ratio:
2025-07-01 02:59:59.954 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 02:59:59.962 if best_ratio < cutoff:
2025-07-01 02:59:59.982 # no non-identical "pretty close" pair
2025-07-01 02:59:59.990 if eqi is None:
2025-07-01 03:00:00.005 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:00.015 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:00.034 return
2025-07-01 03:00:00.046 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:00.062 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:00.078 else:
2025-07-01 03:00:00.097 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:00.110 eqi = None
2025-07-01 03:00:00.126
2025-07-01 03:00:00.138 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:00.151 # identical
2025-07-01 03:00:00.163
2025-07-01 03:00:00.181 # pump out diffs from before the synch point
2025-07-01 03:00:00.193 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:00.207
2025-07-01 03:00:00.219 # do intraline marking on the synch pair
2025-07-01 03:00:00.239 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:00.253 if eqi is None:
2025-07-01 03:00:00.272 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:00.291 atags = btags = ""
2025-07-01 03:00:00.302 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:00.320 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:00.334 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:00.347 if tag == 'replace':
2025-07-01 03:00:00.373 atags += '^' * la
2025-07-01 03:00:00.389 btags += '^' * lb
2025-07-01 03:00:00.399 elif tag == 'delete':
2025-07-01 03:00:00.415 atags += '-' * la
2025-07-01 03:00:00.425 elif tag == 'insert':
2025-07-01 03:00:00.437 btags += '+' * lb
2025-07-01 03:00:00.453 elif tag == 'equal':
2025-07-01 03:00:00.466 atags += ' ' * la
2025-07-01 03:00:00.478 btags += ' ' * lb
2025-07-01 03:00:00.487 else:
2025-07-01 03:00:00.504 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:00.522 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:00.530 else:
2025-07-01 03:00:00.538 # the synch pair is identical
2025-07-01 03:00:00.554 yield '  ' + aelt
2025-07-01 03:00:00.562
2025-07-01 03:00:00.578 # pump out diffs from after the synch point
2025-07-01 03:00:00.586 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:00.598
2025-07-01 03:00:00.614 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:00.630 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:00.641
2025-07-01 03:00:00.658 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:00.674 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:00.682 alo = 66, ahi = 1101
2025-07-01 03:00:00.697 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:00.706 blo = 66, bhi = 1101
2025-07-01 03:00:00.721
2025-07-01 03:00:00.734 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:00.742 g = []
2025-07-01 03:00:00.754 if alo < ahi:
2025-07-01 03:00:00.762 if blo < bhi:
2025-07-01 03:00:00.774 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:00.786 else:
2025-07-01 03:00:00.794 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:00.806 elif blo < bhi:
2025-07-01 03:00:00.822 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:00.830
2025-07-01 03:00:00.846 >       yield from g
2025-07-01 03:00:00.854
2025-07-01 03:00:00.869 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:00.878 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:00.894
2025-07-01 03:00:00.902 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:00.914 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:00.933 alo = 66, ahi = 1101
2025-07-01 03:00:00.950 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:00.966 blo = 66, bhi = 1101
2025-07-01 03:00:00.982
2025-07-01 03:00:00.993 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:01.009 r"""
2025-07-01 03:00:01.022 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:01.034 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:01.046 synch point, and intraline difference marking is done on the
2025-07-01 03:00:01.058 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:01.073
2025-07-01 03:00:01.086 Example:
2025-07-01 03:00:01.098
2025-07-01 03:00:01.110 >>> d = Differ()
2025-07-01 03:00:01.118 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:01.134 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:01.142 >>> print(''.join(results), end="")
2025-07-01 03:00:01.158 - abcDefghiJkl
2025-07-01 03:00:01.182 + abcdefGhijkl
2025-07-01 03:00:01.205 """
2025-07-01 03:00:01.217
2025-07-01 03:00:01.232 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:01.250 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:01.262 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:01.274 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:01.286 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:01.303
2025-07-01 03:00:01.314 # search for the pair that matches best without being identical
2025-07-01 03:00:01.330 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:01.342 # on junk -- unless we have to)
2025-07-01 03:00:01.354 for j in range(blo, bhi):
2025-07-01 03:00:01.366 bj = b[j]
2025-07-01 03:00:01.382 cruncher.set_seq2(bj)
2025-07-01 03:00:01.390 for i in range(alo, ahi):
2025-07-01 03:00:01.411 ai = a[i]
2025-07-01 03:00:01.426 if ai == bj:
2025-07-01 03:00:01.434 if eqi is None:
2025-07-01 03:00:01.446 eqi, eqj = i, j
2025-07-01 03:00:01.458 continue
2025-07-01 03:00:01.469 cruncher.set_seq1(ai)
2025-07-01 03:00:01.478 # computing similarity is expensive, so use the quick
2025-07-01 03:00:01.495 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:01.511 # compares by a factor of 3.
2025-07-01 03:00:01.527 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:01.538 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:01.554 # of the computation is cached by cruncher
2025-07-01 03:00:01.570 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:01.582 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:01.594 cruncher.ratio() > best_ratio:
2025-07-01 03:00:01.610 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:01.622 if best_ratio < cutoff:
2025-07-01 03:00:01.635 # no non-identical "pretty close" pair
2025-07-01 03:00:01.649 if eqi is None:
2025-07-01 03:00:01.659 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:01.677 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:01.687 return
2025-07-01 03:00:01.705 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:01.723 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:01.734 else:
2025-07-01 03:00:01.755 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:01.771 eqi = None
2025-07-01 03:00:01.787
2025-07-01 03:00:01.803 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:01.819 # identical
2025-07-01 03:00:01.835
2025-07-01 03:00:01.851 # pump out diffs from before the synch point
2025-07-01 03:00:01.863 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:01.875
2025-07-01 03:00:01.887 # do intraline marking on the synch pair
2025-07-01 03:00:01.907 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:01.922 if eqi is None:
2025-07-01 03:00:01.939 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:01.954 atags = btags = ""
2025-07-01 03:00:01.970 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:01.982 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:01.994 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:02.010 if tag == 'replace':
2025-07-01 03:00:02.030 atags += '^' * la
2025-07-01 03:00:02.047 btags += '^' * lb
2025-07-01 03:00:02.058 elif tag == 'delete':
2025-07-01 03:00:02.074 atags += '-' * la
2025-07-01 03:00:02.091 elif tag == 'insert':
2025-07-01 03:00:02.111 btags += '+' * lb
2025-07-01 03:00:02.126 elif tag == 'equal':
2025-07-01 03:00:02.134 atags += ' ' * la
2025-07-01 03:00:02.150 btags += ' ' * lb
2025-07-01 03:00:02.166 else:
2025-07-01 03:00:02.178 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:02.199 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:02.210 else:
2025-07-01 03:00:02.226 # the synch pair is identical
2025-07-01 03:00:02.242 yield '  ' + aelt
2025-07-01 03:00:02.253
2025-07-01 03:00:02.270 # pump out diffs from after the synch point
2025-07-01 03:00:02.287 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:02.302
2025-07-01 03:00:02.314 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:02.330 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:02.338
2025-07-01 03:00:02.350 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:02.374 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:02.387 alo = 67, ahi = 1101
2025-07-01 03:00:02.402 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:02.410 blo = 67, bhi = 1101
2025-07-01 03:00:02.427
2025-07-01 03:00:02.434 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:02.449 g = []
2025-07-01 03:00:02.467 if alo < ahi:
2025-07-01 03:00:02.474 if blo < bhi:
2025-07-01 03:00:02.490 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:02.498 else:
2025-07-01 03:00:02.518 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:02.535 elif blo < bhi:
2025-07-01 03:00:02.548 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:02.559
2025-07-01 03:00:02.578 >       yield from g
2025-07-01 03:00:02.590
2025-07-01 03:00:02.605 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:02.621 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:02.631
2025-07-01 03:00:02.643 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:02.659 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:02.679 alo = 67, ahi = 1101
2025-07-01 03:00:02.693 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:02.707 blo = 67, bhi = 1101
2025-07-01 03:00:02.722
2025-07-01 03:00:02.731 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:02.750 r"""
2025-07-01 03:00:02.762 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:02.778 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:02.794 synch point, and intraline difference marking is done on the
2025-07-01 03:00:02.810 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:02.826
2025-07-01 03:00:02.846 Example:
2025-07-01 03:00:02.857
2025-07-01 03:00:02.866 >>> d = Differ()
2025-07-01 03:00:02.878 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:02.890 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:02.903 >>> print(''.join(results), end="")
2025-07-01 03:00:02.919 - abcDefghiJkl
2025-07-01 03:00:02.931 + abcdefGhijkl
2025-07-01 03:00:02.943 """
2025-07-01 03:00:02.952
2025-07-01 03:00:02.966 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:02.978 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:02.997 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:03.011 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:03.030 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:03.050
2025-07-01 03:00:03.062 # search for the pair that matches best without being identical
2025-07-01 03:00:03.074 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:03.090 # on junk -- unless we have to)
2025-07-01 03:00:03.101 for j in range(blo, bhi):
2025-07-01 03:00:03.114 bj = b[j]
2025-07-01 03:00:03.134 cruncher.set_seq2(bj)
2025-07-01 03:00:03.142 for i in range(alo, ahi):
2025-07-01 03:00:03.158 ai = a[i]
2025-07-01 03:00:03.169 if ai == bj:
2025-07-01 03:00:03.186 if eqi is None:
2025-07-01 03:00:03.194 eqi, eqj = i, j
2025-07-01 03:00:03.206 continue
2025-07-01 03:00:03.218 cruncher.set_seq1(ai)
2025-07-01 03:00:03.230 # computing similarity is expensive, so use the quick
2025-07-01 03:00:03.246 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:03.258 # compares by a factor of 3.
2025-07-01 03:00:03.274 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:03.285 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:03.294 # of the computation is cached by cruncher
2025-07-01 03:00:03.314 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:03.326 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:03.338 cruncher.ratio() > best_ratio:
2025-07-01 03:00:03.350 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:03.366 if best_ratio < cutoff:
2025-07-01 03:00:03.382 # no non-identical "pretty close" pair
2025-07-01 03:00:03.395 if eqi is None:
2025-07-01 03:00:03.409 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:03.420 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:03.438 return
2025-07-01 03:00:03.454 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:03.470 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:03.486 else:
2025-07-01 03:00:03.502 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:03.510 eqi = None
2025-07-01 03:00:03.518
2025-07-01 03:00:03.534 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:03.550 # identical
2025-07-01 03:00:03.566
2025-07-01 03:00:03.579 # pump out diffs from before the synch point
2025-07-01 03:00:03.596 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:03.601
2025-07-01 03:00:03.607 # do intraline marking on the synch pair
2025-07-01 03:00:03.614 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:03.619 if eqi is None:
2025-07-01 03:00:03.625 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:03.630 atags = btags = ""
2025-07-01 03:00:03.635 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:03.642 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:03.648 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:03.654 if tag == 'replace':
2025-07-01 03:00:03.659 atags += '^' * la
2025-07-01 03:00:03.665 btags += '^' * lb
2025-07-01 03:00:03.670 elif tag == 'delete':
2025-07-01 03:00:03.675 atags += '-' * la
2025-07-01 03:00:03.680 elif tag == 'insert':
2025-07-01 03:00:03.685 btags += '+' * lb
2025-07-01 03:00:03.691 elif tag == 'equal':
2025-07-01 03:00:03.696 atags += ' ' * la
2025-07-01 03:00:03.702 btags += ' ' * lb
2025-07-01 03:00:03.707 else:
2025-07-01 03:00:03.713 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:03.718 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:03.723 else:
2025-07-01 03:00:03.728 # the synch pair is identical
2025-07-01 03:00:03.733 yield '  ' + aelt
2025-07-01 03:00:03.737
2025-07-01 03:00:03.742 # pump out diffs from after the synch point
2025-07-01 03:00:03.747 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:03.752
2025-07-01 03:00:03.757 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:03.763 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:03.768
2025-07-01 03:00:03.773 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:03.778 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:03.783 alo = 70, ahi = 1101
2025-07-01 03:00:03.789 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:03.794 blo = 70, bhi = 1101
2025-07-01 03:00:03.798
2025-07-01 03:00:03.803 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:03.807 g = []
2025-07-01 03:00:03.812 if alo < ahi:
2025-07-01 03:00:03.817 if blo < bhi:
2025-07-01 03:00:03.822 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:03.827 else:
2025-07-01 03:00:03.832 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:03.836 elif blo < bhi:
2025-07-01 03:00:03.841 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:03.846
2025-07-01 03:00:03.850 >       yield from g
2025-07-01 03:00:03.855
2025-07-01 03:00:03.860 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:03.865 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:03.869
2025-07-01 03:00:03.874 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:03.880 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:03.885 alo = 70, ahi = 1101
2025-07-01 03:00:03.890 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:03.895 blo = 70, bhi = 1101
2025-07-01 03:00:03.899
2025-07-01 03:00:03.904 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:03.909 r"""
2025-07-01 03:00:03.914 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:03.919 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:03.924 synch point, and intraline difference marking is done on the
2025-07-01 03:00:03.929 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:03.934
2025-07-01 03:00:03.938 Example:
2025-07-01 03:00:03.943
2025-07-01 03:00:03.947 >>> d = Differ()
2025-07-01 03:00:03.952 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:03.957 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:03.962 >>> print(''.join(results), end="")
2025-07-01 03:00:03.967 - abcDefghiJkl
2025-07-01 03:00:03.976 + abcdefGhijkl
2025-07-01 03:00:03.986 """
2025-07-01 03:00:03.991
2025-07-01 03:00:03.996 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:04.001 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:04.007 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:04.012 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:04.017 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:04.022
2025-07-01 03:00:04.026 # search for the pair that matches best without being identical
2025-07-01 03:00:04.032 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:04.037 # on junk -- unless we have to)
2025-07-01 03:00:04.042 for j in range(blo, bhi):
2025-07-01 03:00:04.047 bj = b[j]
2025-07-01 03:00:04.051 cruncher.set_seq2(bj)
2025-07-01 03:00:04.056 for i in range(alo, ahi):
2025-07-01 03:00:04.061 ai = a[i]
2025-07-01 03:00:04.066 if ai == bj:
2025-07-01 03:00:04.071 if eqi is None:
2025-07-01 03:00:04.075 eqi, eqj = i, j
2025-07-01 03:00:04.080 continue
2025-07-01 03:00:04.086 cruncher.set_seq1(ai)
2025-07-01 03:00:04.091 # computing similarity is expensive, so use the quick
2025-07-01 03:00:04.096 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:04.101 # compares by a factor of 3.
2025-07-01 03:00:04.105 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:04.110 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:04.115 # of the computation is cached by cruncher
2025-07-01 03:00:04.121 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:04.127 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:04.133 cruncher.ratio() > best_ratio:
2025-07-01 03:00:04.140 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:04.146 if best_ratio < cutoff:
2025-07-01 03:00:04.153 # no non-identical "pretty close" pair
2025-07-01 03:00:04.158 if eqi is None:
2025-07-01 03:00:04.163 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:04.168 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:04.173 return
2025-07-01 03:00:04.178 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:04.183 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:04.188 else:
2025-07-01 03:00:04.195 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:04.201 eqi = None
2025-07-01 03:00:04.207
2025-07-01 03:00:04.212 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:04.218 # identical
2025-07-01 03:00:04.237
2025-07-01 03:00:04.246 # pump out diffs from before the synch point
2025-07-01 03:00:04.258 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:04.270
2025-07-01 03:00:04.282 # do intraline marking on the synch pair
2025-07-01 03:00:04.289 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:04.302 if eqi is None:
2025-07-01 03:00:04.318 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:04.326 atags = btags = ""
2025-07-01 03:00:04.338 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:04.358 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:04.366 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:04.377 if tag == 'replace':
2025-07-01 03:00:04.390 atags += '^' * la
2025-07-01 03:00:04.398 btags += '^' * lb
2025-07-01 03:00:04.408 elif tag == 'delete':
2025-07-01 03:00:04.421 atags += '-' * la
2025-07-01 03:00:04.434 elif tag == 'insert':
2025-07-01 03:00:04.450 btags += '+' * lb
2025-07-01 03:00:04.462 elif tag == 'equal':
2025-07-01 03:00:04.470 atags += ' ' * la
2025-07-01 03:00:04.482 btags += ' ' * lb
2025-07-01 03:00:04.498 else:
2025-07-01 03:00:04.518 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:04.535 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:04.547 else:
2025-07-01 03:00:04.567 # the synch pair is identical
2025-07-01 03:00:04.574 yield '  ' + aelt
2025-07-01 03:00:04.591
2025-07-01 03:00:04.607 # pump out diffs from after the synch point
2025-07-01 03:00:04.622 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:04.638
2025-07-01 03:00:04.654 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:04.676 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:04.690
2025-07-01 03:00:04.706 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:04.731 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:04.738 alo = 71, ahi = 1101
2025-07-01 03:00:04.754 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:04.770 blo = 71, bhi = 1101
2025-07-01 03:00:04.783
2025-07-01 03:00:04.806 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:04.814 g = []
2025-07-01 03:00:04.834 if alo < ahi:
2025-07-01 03:00:04.851 if blo < bhi:
2025-07-01 03:00:04.862 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:04.875 else:
2025-07-01 03:00:04.892 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:04.903 elif blo < bhi:
2025-07-01 03:00:04.922 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:04.938
2025-07-01 03:00:04.952 >       yield from g
2025-07-01 03:00:04.968
2025-07-01 03:00:04.978 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:04.990 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:05.010
2025-07-01 03:00:05.029 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:05.038 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:05.057 alo = 71, ahi = 1101
2025-07-01 03:00:05.067 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:05.081 blo = 71, bhi = 1101
2025-07-01 03:00:05.094
2025-07-01 03:00:05.114 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:05.124 r"""
2025-07-01 03:00:05.142 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:05.154 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:05.170 synch point, and intraline difference marking is done on the
2025-07-01 03:00:05.182 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:05.194
2025-07-01 03:00:05.206 Example:
2025-07-01 03:00:05.214
2025-07-01 03:00:05.229 >>> d = Differ()
2025-07-01 03:00:05.238 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:05.249 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:05.262 >>> print(''.join(results), end="")
2025-07-01 03:00:05.274 - abcDefghiJkl
2025-07-01 03:00:05.294 + abcdefGhijkl
2025-07-01 03:00:05.318 """
2025-07-01 03:00:05.334
2025-07-01 03:00:05.342 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:05.354 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:05.362 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:05.375 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:05.390 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:05.402
2025-07-01 03:00:05.410 # search for the pair that matches best without being identical
2025-07-01 03:00:05.420 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:05.433 # on junk -- unless we have to)
2025-07-01 03:00:05.442 for j in range(blo, bhi):
2025-07-01 03:00:05.453 bj = b[j]
2025-07-01 03:00:05.461 cruncher.set_seq2(bj)
2025-07-01 03:00:05.477 for i in range(alo, ahi):
2025-07-01 03:00:05.488 ai = a[i]
2025-07-01 03:00:05.508 if ai == bj:
2025-07-01 03:00:05.521 if eqi is None:
2025-07-01 03:00:05.535 eqi, eqj = i, j
2025-07-01 03:00:05.546 continue
2025-07-01 03:00:05.558 cruncher.set_seq1(ai)
2025-07-01 03:00:05.573 # computing similarity is expensive, so use the quick
2025-07-01 03:00:05.590 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:05.602 # compares by a factor of 3.
2025-07-01 03:00:05.614 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:05.625 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:05.640 # of the computation is cached by cruncher
2025-07-01 03:00:05.654 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:05.669 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:05.681 cruncher.ratio() > best_ratio:
2025-07-01 03:00:05.693 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:05.714 if best_ratio < cutoff:
2025-07-01 03:00:05.725 # no non-identical "pretty close" pair
2025-07-01 03:00:05.738 if eqi is None:
2025-07-01 03:00:05.755 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:05.763 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:05.770 return
2025-07-01 03:00:05.775 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:05.780 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:05.786 else:
2025-07-01 03:00:05.791 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:05.795 eqi = None
2025-07-01 03:00:05.800
2025-07-01 03:00:05.806 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:05.812 # identical
2025-07-01 03:00:05.817
2025-07-01 03:00:05.824 # pump out diffs from before the synch point
2025-07-01 03:00:05.837 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:05.850
2025-07-01 03:00:05.866 # do intraline marking on the synch pair
2025-07-01 03:00:05.878 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:05.890 if eqi is None:
2025-07-01 03:00:05.902 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:05.914 atags = btags = ""
2025-07-01 03:00:05.922 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:05.934 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:05.949 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:05.963 if tag == 'replace':
2025-07-01 03:00:05.978 atags += '^' * la
2025-07-01 03:00:05.994 btags += '^' * lb
2025-07-01 03:00:06.008 elif tag == 'delete':
2025-07-01 03:00:06.026 atags += '-' * la
2025-07-01 03:00:06.038 elif tag == 'insert':
2025-07-01 03:00:06.054 btags += '+' * lb
2025-07-01 03:00:06.065 elif tag == 'equal':
2025-07-01 03:00:06.074 atags += ' ' * la
2025-07-01 03:00:06.090 btags += ' ' * lb
2025-07-01 03:00:06.098 else:
2025-07-01 03:00:06.110 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:06.118 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:06.130 else:
2025-07-01 03:00:06.141 # the synch pair is identical
2025-07-01 03:00:06.155 yield '  ' + aelt
2025-07-01 03:00:06.171
2025-07-01 03:00:06.187 # pump out diffs from after the synch point
2025-07-01 03:00:06.194 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:06.214
2025-07-01 03:00:06.230 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:06.238 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:06.257
2025-07-01 03:00:06.274 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:06.282 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:06.294 alo = 72, ahi = 1101
2025-07-01 03:00:06.305 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:06.318 blo = 72, bhi = 1101
2025-07-01 03:00:06.334
2025-07-01 03:00:06.343 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:06.363 g = []
2025-07-01 03:00:06.379 if alo < ahi:
2025-07-01 03:00:06.390 if blo < bhi:
2025-07-01 03:00:06.406 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:06.419 else:
2025-07-01 03:00:06.434 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:06.440 elif blo < bhi:
2025-07-01 03:00:06.450 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:06.462
2025-07-01 03:00:06.474 >       yield from g
2025-07-01 03:00:06.495
2025-07-01 03:00:06.511 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:06.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:06.538
2025-07-01 03:00:06.552 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:06.562 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:06.578 alo = 72, ahi = 1101
2025-07-01 03:00:06.598 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:06.609 blo = 72, bhi = 1101
2025-07-01 03:00:06.629
2025-07-01 03:00:06.638 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:06.658 r"""
2025-07-01 03:00:06.670 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:06.678 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:06.694 synch point, and intraline difference marking is done on the
2025-07-01 03:00:06.702 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:06.714
2025-07-01 03:00:06.730 Example:
2025-07-01 03:00:06.743
2025-07-01 03:00:06.761 >>> d = Differ()
2025-07-01 03:00:06.778 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:06.794 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:06.805 >>> print(''.join(results), end="")
2025-07-01 03:00:06.817 - abcDefghiJkl
2025-07-01 03:00:06.826 + abcdefGhijkl
2025-07-01 03:00:06.836 """
2025-07-01 03:00:06.841
2025-07-01 03:00:06.845 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:06.850 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:06.855 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:06.860 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:06.867 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:06.872
2025-07-01 03:00:06.877 # search for the pair that matches best without being identical
2025-07-01 03:00:06.882 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:06.887 # on junk -- unless we have to)
2025-07-01 03:00:06.893 for j in range(blo, bhi):
2025-07-01 03:00:06.898 bj = b[j]
2025-07-01 03:00:06.903 cruncher.set_seq2(bj)
2025-07-01 03:00:06.907 for i in range(alo, ahi):
2025-07-01 03:00:06.912 ai = a[i]
2025-07-01 03:00:06.917 if ai == bj:
2025-07-01 03:00:06.921 if eqi is None:
2025-07-01 03:00:06.926 eqi, eqj = i, j
2025-07-01 03:00:06.931 continue
2025-07-01 03:00:06.936 cruncher.set_seq1(ai)
2025-07-01 03:00:06.940 # computing similarity is expensive, so use the quick
2025-07-01 03:00:06.945 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:06.950 # compares by a factor of 3.
2025-07-01 03:00:06.955 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:06.961 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:06.966 # of the computation is cached by cruncher
2025-07-01 03:00:06.971 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:06.978 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:06.984 cruncher.ratio() > best_ratio:
2025-07-01 03:00:06.989 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:06.994 if best_ratio < cutoff:
2025-07-01 03:00:06.999 # no non-identical "pretty close" pair
2025-07-01 03:00:07.005 if eqi is None:
2025-07-01 03:00:07.010 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:07.015 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:07.020 return
2025-07-01 03:00:07.025 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:07.031 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:07.035 else:
2025-07-01 03:00:07.040 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:07.045 eqi = None
2025-07-01 03:00:07.050
2025-07-01 03:00:07.055 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:07.059 # identical
2025-07-01 03:00:07.065
2025-07-01 03:00:07.070 # pump out diffs from before the synch point
2025-07-01 03:00:07.075 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:07.080
2025-07-01 03:00:07.086 # do intraline marking on the synch pair
2025-07-01 03:00:07.091 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:07.095 if eqi is None:
2025-07-01 03:00:07.100 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:07.104 atags = btags = ""
2025-07-01 03:00:07.109 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:07.114 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:07.119 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:07.123 if tag == 'replace':
2025-07-01 03:00:07.128 atags += '^' * la
2025-07-01 03:00:07.133 btags += '^' * lb
2025-07-01 03:00:07.137 elif tag == 'delete':
2025-07-01 03:00:07.142 atags += '-' * la
2025-07-01 03:00:07.147 elif tag == 'insert':
2025-07-01 03:00:07.151 btags += '+' * lb
2025-07-01 03:00:07.159 elif tag == 'equal':
2025-07-01 03:00:07.163 atags += ' ' * la
2025-07-01 03:00:07.169 btags += ' ' * lb
2025-07-01 03:00:07.173 else:
2025-07-01 03:00:07.178 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:07.183 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:07.188 else:
2025-07-01 03:00:07.193 # the synch pair is identical
2025-07-01 03:00:07.199 yield '  ' + aelt
2025-07-01 03:00:07.203
2025-07-01 03:00:07.208 # pump out diffs from after the synch point
2025-07-01 03:00:07.214 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:07.219
2025-07-01 03:00:07.224 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:07.229 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:07.234
2025-07-01 03:00:07.239 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:07.244 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:07.248 alo = 73, ahi = 1101
2025-07-01 03:00:07.254 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:07.259 blo = 73, bhi = 1101
2025-07-01 03:00:07.265
2025-07-01 03:00:07.270 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:07.275 g = []
2025-07-01 03:00:07.280 if alo < ahi:
2025-07-01 03:00:07.285 if blo < bhi:
2025-07-01 03:00:07.290 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:07.295 else:
2025-07-01 03:00:07.301 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:07.306 elif blo < bhi:
2025-07-01 03:00:07.312 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:07.317
2025-07-01 03:00:07.322 >       yield from g
2025-07-01 03:00:07.328
2025-07-01 03:00:07.333 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:07.338 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:07.344
2025-07-01 03:00:07.349 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:07.354 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:07.359 alo = 73, ahi = 1101
2025-07-01 03:00:07.364 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:07.368 blo = 73, bhi = 1101
2025-07-01 03:00:07.373
2025-07-01 03:00:07.378 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:07.383 r"""
2025-07-01 03:00:07.389 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:07.394 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:07.400 synch point, and intraline difference marking is done on the
2025-07-01 03:00:07.405 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:07.410
2025-07-01 03:00:07.415 Example:
2025-07-01 03:00:07.421
2025-07-01 03:00:07.426 >>> d = Differ()
2025-07-01 03:00:07.432 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:07.437 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:07.443 >>> print(''.join(results), end="")
2025-07-01 03:00:07.449 - abcDefghiJkl
2025-07-01 03:00:07.461 + abcdefGhijkl
2025-07-01 03:00:07.471 """
2025-07-01 03:00:07.476
2025-07-01 03:00:07.482 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:07.488 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:07.493 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:07.499 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:07.504 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:07.510
2025-07-01 03:00:07.516 # search for the pair that matches best without being identical
2025-07-01 03:00:07.522 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:07.528 # on junk -- unless we have to)
2025-07-01 03:00:07.534 for j in range(blo, bhi):
2025-07-01 03:00:07.539 bj = b[j]
2025-07-01 03:00:07.545 cruncher.set_seq2(bj)
2025-07-01 03:00:07.550 for i in range(alo, ahi):
2025-07-01 03:00:07.556 ai = a[i]
2025-07-01 03:00:07.562 if ai == bj:
2025-07-01 03:00:07.567 if eqi is None:
2025-07-01 03:00:07.573 eqi, eqj = i, j
2025-07-01 03:00:07.578 continue
2025-07-01 03:00:07.584 cruncher.set_seq1(ai)
2025-07-01 03:00:07.589 # computing similarity is expensive, so use the quick
2025-07-01 03:00:07.596 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:07.600 # compares by a factor of 3.
2025-07-01 03:00:07.605 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:07.610 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:07.615 # of the computation is cached by cruncher
2025-07-01 03:00:07.621 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:07.627 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:07.633 cruncher.ratio() > best_ratio:
2025-07-01 03:00:07.639 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:07.645 if best_ratio < cutoff:
2025-07-01 03:00:07.650 # no non-identical "pretty close" pair
2025-07-01 03:00:07.655 if eqi is None:
2025-07-01 03:00:07.661 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:07.667 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:07.672 return
2025-07-01 03:00:07.678 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:07.685 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:07.690 else:
2025-07-01 03:00:07.695 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:07.700 eqi = None
2025-07-01 03:00:07.705
2025-07-01 03:00:07.710 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:07.715 # identical
2025-07-01 03:00:07.719
2025-07-01 03:00:07.724 # pump out diffs from before the synch point
2025-07-01 03:00:07.729 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:07.734
2025-07-01 03:00:07.739 # do intraline marking on the synch pair
2025-07-01 03:00:07.744 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:07.749 if eqi is None:
2025-07-01 03:00:07.754 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:07.759 atags = btags = ""
2025-07-01 03:00:07.764 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:07.769 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:07.774 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:07.779 if tag == 'replace':
2025-07-01 03:00:07.784 atags += '^' * la
2025-07-01 03:00:07.789 btags += '^' * lb
2025-07-01 03:00:07.794 elif tag == 'delete':
2025-07-01 03:00:07.799 atags += '-' * la
2025-07-01 03:00:07.804 elif tag == 'insert':
2025-07-01 03:00:07.809 btags += '+' * lb
2025-07-01 03:00:07.814 elif tag == 'equal':
2025-07-01 03:00:07.819 atags += ' ' * la
2025-07-01 03:00:07.825 btags += ' ' * lb
2025-07-01 03:00:07.831 else:
2025-07-01 03:00:07.836 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:07.842 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:07.847 else:
2025-07-01 03:00:07.852 # the synch pair is identical
2025-07-01 03:00:07.856 yield '  ' + aelt
2025-07-01 03:00:07.862
2025-07-01 03:00:07.868 # pump out diffs from after the synch point
2025-07-01 03:00:07.875 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:07.880
2025-07-01 03:00:07.886 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:07.891 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:07.897
2025-07-01 03:00:07.903 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:07.908 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:07.914 alo = 74, ahi = 1101
2025-07-01 03:00:07.919 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:07.924 blo = 74, bhi = 1101
2025-07-01 03:00:07.929
2025-07-01 03:00:07.935 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:07.940 g = []
2025-07-01 03:00:07.945 if alo < ahi:
2025-07-01 03:00:07.951 if blo < bhi:
2025-07-01 03:00:07.955 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:07.960 else:
2025-07-01 03:00:07.964 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:07.969 elif blo < bhi:
2025-07-01 03:00:07.974 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:07.978
2025-07-01 03:00:07.982 >       yield from g
2025-07-01 03:00:07.987
2025-07-01 03:00:07.992 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:07.997 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:08.002
2025-07-01 03:00:08.008 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:08.013 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:08.018 alo = 74, ahi = 1101
2025-07-01 03:00:08.023 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:08.028 blo = 74, bhi = 1101
2025-07-01 03:00:08.033
2025-07-01 03:00:08.038 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:08.043 r"""
2025-07-01 03:00:08.048 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:08.054 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:08.059 synch point, and intraline difference marking is done on the
2025-07-01 03:00:08.063 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:08.068
2025-07-01 03:00:08.072 Example:
2025-07-01 03:00:08.077
2025-07-01 03:00:08.081 >>> d = Differ()
2025-07-01 03:00:08.086 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:08.091 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:08.096 >>> print(''.join(results), end="")
2025-07-01 03:00:08.101 - abcDefghiJkl
2025-07-01 03:00:08.110 + abcdefGhijkl
2025-07-01 03:00:08.119 """
2025-07-01 03:00:08.126
2025-07-01 03:00:08.131 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:08.135 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:08.140 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:08.147 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:08.152 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:08.156
2025-07-01 03:00:08.161 # search for the pair that matches best without being identical
2025-07-01 03:00:08.165 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:08.170 # on junk -- unless we have to)
2025-07-01 03:00:08.174 for j in range(blo, bhi):
2025-07-01 03:00:08.179 bj = b[j]
2025-07-01 03:00:08.183 cruncher.set_seq2(bj)
2025-07-01 03:00:08.188 for i in range(alo, ahi):
2025-07-01 03:00:08.192 ai = a[i]
2025-07-01 03:00:08.197 if ai == bj:
2025-07-01 03:00:08.201 if eqi is None:
2025-07-01 03:00:08.206 eqi, eqj = i, j
2025-07-01 03:00:08.210 continue
2025-07-01 03:00:08.214 cruncher.set_seq1(ai)
2025-07-01 03:00:08.219 # computing similarity is expensive, so use the quick
2025-07-01 03:00:08.223 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:08.227 # compares by a factor of 3.
2025-07-01 03:00:08.232 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:08.236 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:08.241 # of the computation is cached by cruncher
2025-07-01 03:00:08.245 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:08.250 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:08.254 cruncher.ratio() > best_ratio:
2025-07-01 03:00:08.259 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:08.263 if best_ratio < cutoff:
2025-07-01 03:00:08.267 # no non-identical "pretty close" pair
2025-07-01 03:00:08.271 if eqi is None:
2025-07-01 03:00:08.276 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:08.280 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:08.285 return
2025-07-01 03:00:08.289 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:08.294 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:08.298 else:
2025-07-01 03:00:08.302 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:08.307 eqi = None
2025-07-01 03:00:08.311
2025-07-01 03:00:08.315 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:08.320 # identical
2025-07-01 03:00:08.324
2025-07-01 03:00:08.328 # pump out diffs from before the synch point
2025-07-01 03:00:08.333 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:08.337
2025-07-01 03:00:08.342 # do intraline marking on the synch pair
2025-07-01 03:00:08.349 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:08.353 if eqi is None:
2025-07-01 03:00:08.358 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:08.363 atags = btags = ""
2025-07-01 03:00:08.367 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:08.373 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:08.378 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:08.383 if tag == 'replace':
2025-07-01 03:00:08.388 atags += '^' * la
2025-07-01 03:00:08.393 btags += '^' * lb
2025-07-01 03:00:08.398 elif tag == 'delete':
2025-07-01 03:00:08.402 atags += '-' * la
2025-07-01 03:00:08.407 elif tag == 'insert':
2025-07-01 03:00:08.411 btags += '+' * lb
2025-07-01 03:00:08.416 elif tag == 'equal':
2025-07-01 03:00:08.421 atags += ' ' * la
2025-07-01 03:00:08.425 btags += ' ' * lb
2025-07-01 03:00:08.430 else:
2025-07-01 03:00:08.434 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:08.439 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:08.446 else:
2025-07-01 03:00:08.451 # the synch pair is identical
2025-07-01 03:00:08.456 yield '  ' + aelt
2025-07-01 03:00:08.460
2025-07-01 03:00:08.465 # pump out diffs from after the synch point
2025-07-01 03:00:08.471 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:08.475
2025-07-01 03:00:08.480 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:08.484 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:08.488
2025-07-01 03:00:08.493 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:08.498 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:08.503 alo = 75, ahi = 1101
2025-07-01 03:00:08.507 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:08.513 blo = 75, bhi = 1101
2025-07-01 03:00:08.519
2025-07-01 03:00:08.526 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:08.531 g = []
2025-07-01 03:00:08.537 if alo < ahi:
2025-07-01 03:00:08.542 if blo < bhi:
2025-07-01 03:00:08.547 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:08.551 else:
2025-07-01 03:00:08.556 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:08.560 elif blo < bhi:
2025-07-01 03:00:08.565 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:08.570
2025-07-01 03:00:08.574 >       yield from g
2025-07-01 03:00:08.579
2025-07-01 03:00:08.583 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:08.588 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:08.592
2025-07-01 03:00:08.597 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:08.603 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:08.608 alo = 75, ahi = 1101
2025-07-01 03:00:08.613 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:08.617 blo = 75, bhi = 1101
2025-07-01 03:00:08.621
2025-07-01 03:00:08.626 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:08.631 r"""
2025-07-01 03:00:08.635 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:08.640 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:08.645 synch point, and intraline difference marking is done on the
2025-07-01 03:00:08.650 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:08.655
2025-07-01 03:00:08.659 Example:
2025-07-01 03:00:08.664
2025-07-01 03:00:08.669 >>> d = Differ()
2025-07-01 03:00:08.674 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:08.678 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:08.683 >>> print(''.join(results), end="")
2025-07-01 03:00:08.687 - abcDefghiJkl
2025-07-01 03:00:08.698 + abcdefGhijkl
2025-07-01 03:00:08.708 """
2025-07-01 03:00:08.713
2025-07-01 03:00:08.718 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:08.723 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:08.728 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:08.734 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:08.740 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:08.746
2025-07-01 03:00:08.752 # search for the pair that matches best without being identical
2025-07-01 03:00:08.758 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:08.764 # on junk -- unless we have to)
2025-07-01 03:00:08.770 for j in range(blo, bhi):
2025-07-01 03:00:08.775 bj = b[j]
2025-07-01 03:00:08.781 cruncher.set_seq2(bj)
2025-07-01 03:00:08.787 for i in range(alo, ahi):
2025-07-01 03:00:08.793 ai = a[i]
2025-07-01 03:00:08.799 if ai == bj:
2025-07-01 03:00:08.804 if eqi is None:
2025-07-01 03:00:08.810 eqi, eqj = i, j
2025-07-01 03:00:08.818 continue
2025-07-01 03:00:08.830 cruncher.set_seq1(ai)
2025-07-01 03:00:08.837 # computing similarity is expensive, so use the quick
2025-07-01 03:00:08.844 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:08.850 # compares by a factor of 3.
2025-07-01 03:00:08.856 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:08.863 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:08.869 # of the computation is cached by cruncher
2025-07-01 03:00:08.876 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:08.882 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:08.888 cruncher.ratio() > best_ratio:
2025-07-01 03:00:08.895 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:08.901 if best_ratio < cutoff:
2025-07-01 03:00:08.908 # no non-identical "pretty close" pair
2025-07-01 03:00:08.914 if eqi is None:
2025-07-01 03:00:08.921 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:08.928 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:08.934 return
2025-07-01 03:00:08.941 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:08.947 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:08.953 else:
2025-07-01 03:00:08.960 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:08.969 eqi = None
2025-07-01 03:00:08.975
2025-07-01 03:00:08.982 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:08.988 # identical
2025-07-01 03:00:08.994
2025-07-01 03:00:09.001 # pump out diffs from before the synch point
2025-07-01 03:00:09.008 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:09.018
2025-07-01 03:00:09.026 # do intraline marking on the synch pair
2025-07-01 03:00:09.033 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:09.040 if eqi is None:
2025-07-01 03:00:09.047 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:09.059 atags = btags = ""
2025-07-01 03:00:09.076 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:09.084 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:09.091 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:09.097 if tag == 'replace':
2025-07-01 03:00:09.104 atags += '^' * la
2025-07-01 03:00:09.110 btags += '^' * lb
2025-07-01 03:00:09.117 elif tag == 'delete':
2025-07-01 03:00:09.123 atags += '-' * la
2025-07-01 03:00:09.135 elif tag == 'insert':
2025-07-01 03:00:09.154 btags += '+' * lb
2025-07-01 03:00:09.167 elif tag == 'equal':
2025-07-01 03:00:09.183 atags += ' ' * la
2025-07-01 03:00:09.201 btags += ' ' * lb
2025-07-01 03:00:09.214 else:
2025-07-01 03:00:09.231 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:09.259 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:09.277 else:
2025-07-01 03:00:09.290 # the synch pair is identical
2025-07-01 03:00:09.309 yield '  ' + aelt
2025-07-01 03:00:09.325
2025-07-01 03:00:09.335 # pump out diffs from after the synch point
2025-07-01 03:00:09.350 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:09.358
2025-07-01 03:00:09.366 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:09.383 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:09.403
2025-07-01 03:00:09.421 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:09.429 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:09.446 alo = 76, ahi = 1101
2025-07-01 03:00:09.462 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:09.474 blo = 76, bhi = 1101
2025-07-01 03:00:09.482
2025-07-01 03:00:09.490 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:09.506 g = []
2025-07-01 03:00:09.514 if alo < ahi:
2025-07-01 03:00:09.526 if blo < bhi:
2025-07-01 03:00:09.539 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:09.562 else:
2025-07-01 03:00:09.570 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:09.582 elif blo < bhi:
2025-07-01 03:00:09.594 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:09.610
2025-07-01 03:00:09.622 >       yield from g
2025-07-01 03:00:09.630
2025-07-01 03:00:09.642 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:09.658 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:09.670
2025-07-01 03:00:09.682 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:09.698 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:09.710 alo = 76, ahi = 1101
2025-07-01 03:00:09.726 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:09.738 blo = 76, bhi = 1101
2025-07-01 03:00:09.750
2025-07-01 03:00:09.758 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:09.770 r"""
2025-07-01 03:00:09.782 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:09.799 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:09.815 synch point, and intraline difference marking is done on the
2025-07-01 03:00:09.830 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:09.838
2025-07-01 03:00:09.850 Example:
2025-07-01 03:00:09.862
2025-07-01 03:00:09.874 >>> d = Differ()
2025-07-01 03:00:09.882 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:09.898 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:09.914 >>> print(''.join(results), end="")
2025-07-01 03:00:09.926 - abcDefghiJkl
2025-07-01 03:00:09.947 + abcdefGhijkl
2025-07-01 03:00:09.972 """
2025-07-01 03:00:09.982
2025-07-01 03:00:09.993 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:10.005 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:10.018 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:10.029 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:10.042 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:10.050
2025-07-01 03:00:10.066 # search for the pair that matches best without being identical
2025-07-01 03:00:10.078 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:10.090 # on junk -- unless we have to)
2025-07-01 03:00:10.102 for j in range(blo, bhi):
2025-07-01 03:00:10.114 bj = b[j]
2025-07-01 03:00:10.122 cruncher.set_seq2(bj)
2025-07-01 03:00:10.134 for i in range(alo, ahi):
2025-07-01 03:00:10.150 ai = a[i]
2025-07-01 03:00:10.158 if ai == bj:
2025-07-01 03:00:10.166 if eqi is None:
2025-07-01 03:00:10.178 eqi, eqj = i, j
2025-07-01 03:00:10.194 continue
2025-07-01 03:00:10.202 cruncher.set_seq1(ai)
2025-07-01 03:00:10.214 # computing similarity is expensive, so use the quick
2025-07-01 03:00:10.222 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:10.238 # compares by a factor of 3.
2025-07-01 03:00:10.246 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:10.258 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:10.271 # of the computation is cached by cruncher
2025-07-01 03:00:10.282 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:10.298 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:10.314 cruncher.ratio() > best_ratio:
2025-07-01 03:00:10.330 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:10.346 if best_ratio < cutoff:
2025-07-01 03:00:10.354 # no non-identical "pretty close" pair
2025-07-01 03:00:10.366 if eqi is None:
2025-07-01 03:00:10.378 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:10.386 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:10.398 return
2025-07-01 03:00:10.406 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:10.418 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:10.430 else:
2025-07-01 03:00:10.438 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:10.450 eqi = None
2025-07-01 03:00:10.458
2025-07-01 03:00:10.470 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:10.478 # identical
2025-07-01 03:00:10.494
2025-07-01 03:00:10.506 # pump out diffs from before the synch point
2025-07-01 03:00:10.518 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:10.530
2025-07-01 03:00:10.542 # do intraline marking on the synch pair
2025-07-01 03:00:10.554 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:10.564 if eqi is None:
2025-07-01 03:00:10.578 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:10.593 atags = btags = ""
2025-07-01 03:00:10.602 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:10.622 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:10.638 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:10.655 if tag == 'replace':
2025-07-01 03:00:10.666 atags += '^' * la
2025-07-01 03:00:10.678 btags += '^' * lb
2025-07-01 03:00:10.689 elif tag == 'delete':
2025-07-01 03:00:10.698 atags += '-' * la
2025-07-01 03:00:10.710 elif tag == 'insert':
2025-07-01 03:00:10.722 btags += '+' * lb
2025-07-01 03:00:10.730 elif tag == 'equal':
2025-07-01 03:00:10.742 atags += ' ' * la
2025-07-01 03:00:10.754 btags += ' ' * lb
2025-07-01 03:00:10.774 else:
2025-07-01 03:00:10.782 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:10.789 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:10.793 else:
2025-07-01 03:00:10.806 # the synch pair is identical
2025-07-01 03:00:10.811 yield '  ' + aelt
2025-07-01 03:00:10.826
2025-07-01 03:00:10.838 # pump out diffs from after the synch point
2025-07-01 03:00:10.846 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:10.858
2025-07-01 03:00:10.870 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:10.882 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:10.890
2025-07-01 03:00:10.905 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:10.918 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:10.930 alo = 77, ahi = 1101
2025-07-01 03:00:10.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:10.950 blo = 77, bhi = 1101
2025-07-01 03:00:10.958
2025-07-01 03:00:10.970 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:10.978 g = []
2025-07-01 03:00:10.989 if alo < ahi:
2025-07-01 03:00:11.002 if blo < bhi:
2025-07-01 03:00:11.014 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:11.026 else:
2025-07-01 03:00:11.033 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:11.050 elif blo < bhi:
2025-07-01 03:00:11.062 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:11.074
2025-07-01 03:00:11.089 >       yield from g
2025-07-01 03:00:11.106
2025-07-01 03:00:11.122 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:11.138 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:11.154
2025-07-01 03:00:11.165 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:11.172 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:11.177 alo = 77, ahi = 1101
2025-07-01 03:00:11.182 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:11.188 blo = 77, bhi = 1101
2025-07-01 03:00:11.194
2025-07-01 03:00:11.200 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:11.206 r"""
2025-07-01 03:00:11.211 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:11.215 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:11.220 synch point, and intraline difference marking is done on the
2025-07-01 03:00:11.225 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:11.229
2025-07-01 03:00:11.233 Example:
2025-07-01 03:00:11.238
2025-07-01 03:00:11.242 >>> d = Differ()
2025-07-01 03:00:11.246 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:11.251 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:11.255 >>> print(''.join(results), end="")
2025-07-01 03:00:11.260 - abcDefghiJkl
2025-07-01 03:00:11.269 + abcdefGhijkl
2025-07-01 03:00:11.279 """
2025-07-01 03:00:11.284
2025-07-01 03:00:11.289 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:11.293 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:11.298 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:11.303 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:11.308 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:11.312
2025-07-01 03:00:11.317 # search for the pair that matches best without being identical
2025-07-01 03:00:11.322 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:11.326 # on junk -- unless we have to)
2025-07-01 03:00:11.331 for j in range(blo, bhi):
2025-07-01 03:00:11.335 bj = b[j]
2025-07-01 03:00:11.340 cruncher.set_seq2(bj)
2025-07-01 03:00:11.345 for i in range(alo, ahi):
2025-07-01 03:00:11.349 ai = a[i]
2025-07-01 03:00:11.354 if ai == bj:
2025-07-01 03:00:11.359 if eqi is None:
2025-07-01 03:00:11.364 eqi, eqj = i, j
2025-07-01 03:00:11.370 continue
2025-07-01 03:00:11.376 cruncher.set_seq1(ai)
2025-07-01 03:00:11.383 # computing similarity is expensive, so use the quick
2025-07-01 03:00:11.388 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:11.393 # compares by a factor of 3.
2025-07-01 03:00:11.398 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:11.403 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:11.407 # of the computation is cached by cruncher
2025-07-01 03:00:11.412 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:11.417 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:11.422 cruncher.ratio() > best_ratio:
2025-07-01 03:00:11.427 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:11.433 if best_ratio < cutoff:
2025-07-01 03:00:11.438 # no non-identical "pretty close" pair
2025-07-01 03:00:11.444 if eqi is None:
2025-07-01 03:00:11.449 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:11.454 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:11.460 return
2025-07-01 03:00:11.467 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:11.471 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:11.476 else:
2025-07-01 03:00:11.481 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:11.485 eqi = None
2025-07-01 03:00:11.490
2025-07-01 03:00:11.495 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:11.499 # identical
2025-07-01 03:00:11.503
2025-07-01 03:00:11.508 # pump out diffs from before the synch point
2025-07-01 03:00:11.512 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:11.517
2025-07-01 03:00:11.521 # do intraline marking on the synch pair
2025-07-01 03:00:11.526 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:11.530 if eqi is None:
2025-07-01 03:00:11.535 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:11.541 atags = btags = ""
2025-07-01 03:00:11.547 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:11.552 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:11.558 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:11.563 if tag == 'replace':
2025-07-01 03:00:11.568 atags += '^' * la
2025-07-01 03:00:11.574 btags += '^' * lb
2025-07-01 03:00:11.579 elif tag == 'delete':
2025-07-01 03:00:11.585 atags += '-' * la
2025-07-01 03:00:11.590 elif tag == 'insert':
2025-07-01 03:00:11.596 btags += '+' * lb
2025-07-01 03:00:11.601 elif tag == 'equal':
2025-07-01 03:00:11.606 atags += ' ' * la
2025-07-01 03:00:11.611 btags += ' ' * lb
2025-07-01 03:00:11.617 else:
2025-07-01 03:00:11.623 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:11.629 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:11.633 else:
2025-07-01 03:00:11.638 # the synch pair is identical
2025-07-01 03:00:11.642 yield '  ' + aelt
2025-07-01 03:00:11.646
2025-07-01 03:00:11.653 # pump out diffs from after the synch point
2025-07-01 03:00:11.659 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:11.664
2025-07-01 03:00:11.669 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:11.673 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:11.678
2025-07-01 03:00:11.682 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:11.688 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:11.692 alo = 78, ahi = 1101
2025-07-01 03:00:11.698 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:11.702 blo = 78, bhi = 1101
2025-07-01 03:00:11.707
2025-07-01 03:00:11.711 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:11.716 g = []
2025-07-01 03:00:11.720 if alo < ahi:
2025-07-01 03:00:11.725 if blo < bhi:
2025-07-01 03:00:11.730 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:11.734 else:
2025-07-01 03:00:11.739 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:11.744 elif blo < bhi:
2025-07-01 03:00:11.748 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:11.753
2025-07-01 03:00:11.758 >       yield from g
2025-07-01 03:00:11.762
2025-07-01 03:00:11.767 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:11.772 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:11.776
2025-07-01 03:00:11.781 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:11.786 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:11.791 alo = 78, ahi = 1101
2025-07-01 03:00:11.796 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:11.800 blo = 78, bhi = 1101
2025-07-01 03:00:11.805
2025-07-01 03:00:11.810 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:11.815 r"""
2025-07-01 03:00:11.819 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:11.825 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:11.830 synch point, and intraline difference marking is done on the
2025-07-01 03:00:11.835 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:11.840
2025-07-01 03:00:11.844 Example:
2025-07-01 03:00:11.849
2025-07-01 03:00:11.853 >>> d = Differ()
2025-07-01 03:00:11.858 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:11.862 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:11.867 >>> print(''.join(results), end="")
2025-07-01 03:00:11.871 - abcDefghiJkl
2025-07-01 03:00:11.880 + abcdefGhijkl
2025-07-01 03:00:11.889 """
2025-07-01 03:00:11.894
2025-07-01 03:00:11.898 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:11.903 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:11.907 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:11.912 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:11.916 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:11.921
2025-07-01 03:00:11.925 # search for the pair that matches best without being identical
2025-07-01 03:00:11.930 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:11.935 # on junk -- unless we have to)
2025-07-01 03:00:11.939 for j in range(blo, bhi):
2025-07-01 03:00:11.944 bj = b[j]
2025-07-01 03:00:11.948 cruncher.set_seq2(bj)
2025-07-01 03:00:11.953 for i in range(alo, ahi):
2025-07-01 03:00:11.957 ai = a[i]
2025-07-01 03:00:11.962 if ai == bj:
2025-07-01 03:00:11.966 if eqi is None:
2025-07-01 03:00:11.971 eqi, eqj = i, j
2025-07-01 03:00:11.977 continue
2025-07-01 03:00:11.985 cruncher.set_seq1(ai)
2025-07-01 03:00:11.992 # computing similarity is expensive, so use the quick
2025-07-01 03:00:11.998 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:12.004 # compares by a factor of 3.
2025-07-01 03:00:12.009 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:12.015 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:12.021 # of the computation is cached by cruncher
2025-07-01 03:00:12.026 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:12.031 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:12.036 cruncher.ratio() > best_ratio:
2025-07-01 03:00:12.040 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:12.045 if best_ratio < cutoff:
2025-07-01 03:00:12.050 # no non-identical "pretty close" pair
2025-07-01 03:00:12.055 if eqi is None:
2025-07-01 03:00:12.060 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:12.064 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:12.068 return
2025-07-01 03:00:12.073 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:12.077 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:12.082 else:
2025-07-01 03:00:12.086 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:12.092 eqi = None
2025-07-01 03:00:12.097
2025-07-01 03:00:12.102 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:12.106 # identical
2025-07-01 03:00:12.111
2025-07-01 03:00:12.115 # pump out diffs from before the synch point
2025-07-01 03:00:12.120 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:12.124
2025-07-01 03:00:12.129 # do intraline marking on the synch pair
2025-07-01 03:00:12.134 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:12.138 if eqi is None:
2025-07-01 03:00:12.143 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:12.148 atags = btags = ""
2025-07-01 03:00:12.153 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:12.159 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:12.165 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:12.171 if tag == 'replace':
2025-07-01 03:00:12.178 atags += '^' * la
2025-07-01 03:00:12.184 btags += '^' * lb
2025-07-01 03:00:12.191 elif tag == 'delete':
2025-07-01 03:00:12.197 atags += '-' * la
2025-07-01 03:00:12.204 elif tag == 'insert':
2025-07-01 03:00:12.210 btags += '+' * lb
2025-07-01 03:00:12.216 elif tag == 'equal':
2025-07-01 03:00:12.222 atags += ' ' * la
2025-07-01 03:00:12.228 btags += ' ' * lb
2025-07-01 03:00:12.234 else:
2025-07-01 03:00:12.243 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:12.249 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:12.255 else:
2025-07-01 03:00:12.262 # the synch pair is identical
2025-07-01 03:00:12.268 yield '  ' + aelt
2025-07-01 03:00:12.273
2025-07-01 03:00:12.277 # pump out diffs from after the synch point
2025-07-01 03:00:12.282 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:12.288
2025-07-01 03:00:12.295 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:12.301 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:12.307
2025-07-01 03:00:12.313 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:12.321 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:12.327 alo = 79, ahi = 1101
2025-07-01 03:00:12.334 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:12.341 blo = 79, bhi = 1101
2025-07-01 03:00:12.347
2025-07-01 03:00:12.353 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:12.357 g = []
2025-07-01 03:00:12.362 if alo < ahi:
2025-07-01 03:00:12.366 if blo < bhi:
2025-07-01 03:00:12.371 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:12.375 else:
2025-07-01 03:00:12.380 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:12.385 elif blo < bhi:
2025-07-01 03:00:12.389 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:12.394
2025-07-01 03:00:12.399 >       yield from g
2025-07-01 03:00:12.403
2025-07-01 03:00:12.408 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:12.413 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:12.417
2025-07-01 03:00:12.422 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:12.427 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:12.431 alo = 79, ahi = 1101
2025-07-01 03:00:12.436 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:12.441 blo = 79, bhi = 1101
2025-07-01 03:00:12.445
2025-07-01 03:00:12.450 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:12.455 r"""
2025-07-01 03:00:12.459 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:12.464 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:12.471 synch point, and intraline difference marking is done on the
2025-07-01 03:00:12.476 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:12.481
2025-07-01 03:00:12.485 Example:
2025-07-01 03:00:12.489
2025-07-01 03:00:12.494 >>> d = Differ()
2025-07-01 03:00:12.498 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:12.503 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:12.508 >>> print(''.join(results), end="")
2025-07-01 03:00:12.513 - abcDefghiJkl
2025-07-01 03:00:12.522 + abcdefGhijkl
2025-07-01 03:00:12.531 """
2025-07-01 03:00:12.535
2025-07-01 03:00:12.540 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:12.544 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:12.549 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:12.554 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:12.559 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:12.565
2025-07-01 03:00:12.570 # search for the pair that matches best without being identical
2025-07-01 03:00:12.574 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:12.579 # on junk -- unless we have to)
2025-07-01 03:00:12.583 for j in range(blo, bhi):
2025-07-01 03:00:12.588 bj = b[j]
2025-07-01 03:00:12.593 cruncher.set_seq2(bj)
2025-07-01 03:00:12.597 for i in range(alo, ahi):
2025-07-01 03:00:12.602 ai = a[i]
2025-07-01 03:00:12.607 if ai == bj:
2025-07-01 03:00:12.613 if eqi is None:
2025-07-01 03:00:12.618 eqi, eqj = i, j
2025-07-01 03:00:12.623 continue
2025-07-01 03:00:12.627 cruncher.set_seq1(ai)
2025-07-01 03:00:12.633 # computing similarity is expensive, so use the quick
2025-07-01 03:00:12.638 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:12.642 # compares by a factor of 3.
2025-07-01 03:00:12.648 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:12.653 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:12.657 # of the computation is cached by cruncher
2025-07-01 03:00:12.662 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:12.667 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:12.672 cruncher.ratio() > best_ratio:
2025-07-01 03:00:12.677 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:12.682 if best_ratio < cutoff:
2025-07-01 03:00:12.688 # no non-identical "pretty close" pair
2025-07-01 03:00:12.693 if eqi is None:
2025-07-01 03:00:12.700 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:12.707 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:12.712 return
2025-07-01 03:00:12.717 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:12.721 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:12.726 else:
2025-07-01 03:00:12.731 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:12.736 eqi = None
2025-07-01 03:00:12.741
2025-07-01 03:00:12.745 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:12.750 # identical
2025-07-01 03:00:12.755
2025-07-01 03:00:12.760 # pump out diffs from before the synch point
2025-07-01 03:00:12.766 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:12.772
2025-07-01 03:00:12.776 # do intraline marking on the synch pair
2025-07-01 03:00:12.781 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:12.785 if eqi is None:
2025-07-01 03:00:12.790 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:12.794 atags = btags = ""
2025-07-01 03:00:12.799 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:12.806 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:12.811 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:12.815 if tag == 'replace':
2025-07-01 03:00:12.820 atags += '^' * la
2025-07-01 03:00:12.824 btags += '^' * lb
2025-07-01 03:00:12.829 elif tag == 'delete':
2025-07-01 03:00:12.834 atags += '-' * la
2025-07-01 03:00:12.839 elif tag == 'insert':
2025-07-01 03:00:12.843 btags += '+' * lb
2025-07-01 03:00:12.847 elif tag == 'equal':
2025-07-01 03:00:12.852 atags += ' ' * la
2025-07-01 03:00:12.856 btags += ' ' * lb
2025-07-01 03:00:12.861 else:
2025-07-01 03:00:12.866 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:12.870 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:12.875 else:
2025-07-01 03:00:12.880 # the synch pair is identical
2025-07-01 03:00:12.885 yield '  ' + aelt
2025-07-01 03:00:12.889
2025-07-01 03:00:12.894 # pump out diffs from after the synch point
2025-07-01 03:00:12.898 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:12.902
2025-07-01 03:00:12.907 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:12.911 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:12.916
2025-07-01 03:00:12.920 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:12.925 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:12.930 alo = 80, ahi = 1101
2025-07-01 03:00:12.935 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:12.939 blo = 80, bhi = 1101
2025-07-01 03:00:12.943
2025-07-01 03:00:12.948 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:12.952 g = []
2025-07-01 03:00:12.957 if alo < ahi:
2025-07-01 03:00:12.962 if blo < bhi:
2025-07-01 03:00:12.966 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:12.971 else:
2025-07-01 03:00:12.976 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:12.981 elif blo < bhi:
2025-07-01 03:00:12.985 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:12.990
2025-07-01 03:00:12.995 >       yield from g
2025-07-01 03:00:12.999
2025-07-01 03:00:13.004 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:13.012 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:13.018
2025-07-01 03:00:13.023 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:13.028 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:13.032 alo = 80, ahi = 1101
2025-07-01 03:00:13.037 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:13.042 blo = 80, bhi = 1101
2025-07-01 03:00:13.046
2025-07-01 03:00:13.051 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:13.057 r"""
2025-07-01 03:00:13.062 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:13.066 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:13.071 synch point, and intraline difference marking is done on the
2025-07-01 03:00:13.075 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:13.080
2025-07-01 03:00:13.084 Example:
2025-07-01 03:00:13.088
2025-07-01 03:00:13.094 >>> d = Differ()
2025-07-01 03:00:13.099 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:13.103 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:13.108 >>> print(''.join(results), end="")
2025-07-01 03:00:13.112 - abcDefghiJkl
2025-07-01 03:00:13.121 + abcdefGhijkl
2025-07-01 03:00:13.130 """
2025-07-01 03:00:13.134
2025-07-01 03:00:13.139 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:13.144 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:13.148 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:13.152 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:13.157 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:13.161
2025-07-01 03:00:13.166 # search for the pair that matches best without being identical
2025-07-01 03:00:13.171 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:13.175 # on junk -- unless we have to)
2025-07-01 03:00:13.180 for j in range(blo, bhi):
2025-07-01 03:00:13.184 bj = b[j]
2025-07-01 03:00:13.189 cruncher.set_seq2(bj)
2025-07-01 03:00:13.193 for i in range(alo, ahi):
2025-07-01 03:00:13.198 ai = a[i]
2025-07-01 03:00:13.203 if ai == bj:
2025-07-01 03:00:13.207 if eqi is None:
2025-07-01 03:00:13.212 eqi, eqj = i, j
2025-07-01 03:00:13.219 continue
2025-07-01 03:00:13.228 cruncher.set_seq1(ai)
2025-07-01 03:00:13.237 # computing similarity is expensive, so use the quick
2025-07-01 03:00:13.243 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:13.248 # compares by a factor of 3.
2025-07-01 03:00:13.252 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:13.257 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:13.262 # of the computation is cached by cruncher
2025-07-01 03:00:13.266 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:13.271 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:13.276 cruncher.ratio() > best_ratio:
2025-07-01 03:00:13.280 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:13.285 if best_ratio < cutoff:
2025-07-01 03:00:13.289 # no non-identical "pretty close" pair
2025-07-01 03:00:13.294 if eqi is None:
2025-07-01 03:00:13.298 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:13.303 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:13.307 return
2025-07-01 03:00:13.313 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:13.317 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:13.322 else:
2025-07-01 03:00:13.330 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:13.335 eqi = None
2025-07-01 03:00:13.340
2025-07-01 03:00:13.345 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:13.349 # identical
2025-07-01 03:00:13.354
2025-07-01 03:00:13.359 # pump out diffs from before the synch point
2025-07-01 03:00:13.366 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:13.374
2025-07-01 03:00:13.380 # do intraline marking on the synch pair
2025-07-01 03:00:13.384 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:13.389 if eqi is None:
2025-07-01 03:00:13.394 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:13.400 atags = btags = ""
2025-07-01 03:00:13.404 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:13.410 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:13.418 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:13.426 if tag == 'replace':
2025-07-01 03:00:13.434 atags += '^' * la
2025-07-01 03:00:13.442 btags += '^' * lb
2025-07-01 03:00:13.454 elif tag == 'delete':
2025-07-01 03:00:13.473 atags += '-' * la
2025-07-01 03:00:13.481 elif tag == 'insert':
2025-07-01 03:00:13.490 btags += '+' * lb
2025-07-01 03:00:13.502 elif tag == 'equal':
2025-07-01 03:00:13.513 atags += ' ' * la
2025-07-01 03:00:13.522 btags += ' ' * lb
2025-07-01 03:00:13.530 else:
2025-07-01 03:00:13.539 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:13.553 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:13.558 else:
2025-07-01 03:00:13.570 # the synch pair is identical
2025-07-01 03:00:13.582 yield '  ' + aelt
2025-07-01 03:00:13.603
2025-07-01 03:00:13.621 # pump out diffs from after the synch point
2025-07-01 03:00:13.638 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:13.650
2025-07-01 03:00:13.662 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:13.674 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:13.682
2025-07-01 03:00:13.694 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:13.702 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:13.714 alo = 81, ahi = 1101
2025-07-01 03:00:13.731 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:13.742 blo = 81, bhi = 1101
2025-07-01 03:00:13.750
2025-07-01 03:00:13.758 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:13.770 g = []
2025-07-01 03:00:13.782 if alo < ahi:
2025-07-01 03:00:13.790 if blo < bhi:
2025-07-01 03:00:13.805 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:13.814 else:
2025-07-01 03:00:13.828 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:13.845 elif blo < bhi:
2025-07-01 03:00:13.857 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:13.874
2025-07-01 03:00:13.886 >       yield from g
2025-07-01 03:00:13.894
2025-07-01 03:00:13.909 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:13.922 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:13.934
2025-07-01 03:00:13.942 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:13.954 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:13.962 alo = 81, ahi = 1101
2025-07-01 03:00:13.978 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:13.986 blo = 81, bhi = 1101
2025-07-01 03:00:14.000
2025-07-01 03:00:14.014 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:14.030 r"""
2025-07-01 03:00:14.042 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:14.058 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:14.071 synch point, and intraline difference marking is done on the
2025-07-01 03:00:14.083 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:14.102
2025-07-01 03:00:14.114 Example:
2025-07-01 03:00:14.129
2025-07-01 03:00:14.138 >>> d = Differ()
2025-07-01 03:00:14.150 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:14.163 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:14.182 >>> print(''.join(results), end="")
2025-07-01 03:00:14.190 - abcDefghiJkl
2025-07-01 03:00:14.218 + abcdefGhijkl
2025-07-01 03:00:14.242 """
2025-07-01 03:00:14.250
2025-07-01 03:00:14.266 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:14.282 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:14.300 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:14.310 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:14.318 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:14.330
2025-07-01 03:00:14.342 # search for the pair that matches best without being identical
2025-07-01 03:00:14.354 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:14.370 # on junk -- unless we have to)
2025-07-01 03:00:14.378 for j in range(blo, bhi):
2025-07-01 03:00:14.390 bj = b[j]
2025-07-01 03:00:14.405 cruncher.set_seq2(bj)
2025-07-01 03:00:14.414 for i in range(alo, ahi):
2025-07-01 03:00:14.426 ai = a[i]
2025-07-01 03:00:14.438 if ai == bj:
2025-07-01 03:00:14.446 if eqi is None:
2025-07-01 03:00:14.458 eqi, eqj = i, j
2025-07-01 03:00:14.466 continue
2025-07-01 03:00:14.478 cruncher.set_seq1(ai)
2025-07-01 03:00:14.490 # computing similarity is expensive, so use the quick
2025-07-01 03:00:14.502 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:14.514 # compares by a factor of 3.
2025-07-01 03:00:14.530 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:14.546 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:14.562 # of the computation is cached by cruncher
2025-07-01 03:00:14.574 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:14.582 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:14.598 cruncher.ratio() > best_ratio:
2025-07-01 03:00:14.614 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:14.626 if best_ratio < cutoff:
2025-07-01 03:00:14.634 # no non-identical "pretty close" pair
2025-07-01 03:00:14.650 if eqi is None:
2025-07-01 03:00:14.658 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:14.675 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:14.685 return
2025-07-01 03:00:14.694 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:14.706 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:14.724 else:
2025-07-01 03:00:14.734 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:14.744 eqi = None
2025-07-01 03:00:14.754
2025-07-01 03:00:14.767 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:14.783 # identical
2025-07-01 03:00:14.789
2025-07-01 03:00:14.799 # pump out diffs from before the synch point
2025-07-01 03:00:14.812 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:14.824
2025-07-01 03:00:14.838 # do intraline marking on the synch pair
2025-07-01 03:00:14.846 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:14.858 if eqi is None:
2025-07-01 03:00:14.870 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:14.882 atags = btags = ""
2025-07-01 03:00:14.895 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:14.910 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:14.922 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:14.934 if tag == 'replace':
2025-07-01 03:00:14.943 atags += '^' * la
2025-07-01 03:00:14.954 btags += '^' * lb
2025-07-01 03:00:14.966 elif tag == 'delete':
2025-07-01 03:00:14.978 atags += '-' * la
2025-07-01 03:00:14.990 elif tag == 'insert':
2025-07-01 03:00:14.996 btags += '+' * lb
2025-07-01 03:00:15.006 elif tag == 'equal':
2025-07-01 03:00:15.017 atags += ' ' * la
2025-07-01 03:00:15.026 btags += ' ' * lb
2025-07-01 03:00:15.041 else:
2025-07-01 03:00:15.052 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:15.062 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:15.078 else:
2025-07-01 03:00:15.090 # the synch pair is identical
2025-07-01 03:00:15.097 yield '  ' + aelt
2025-07-01 03:00:15.108
2025-07-01 03:00:15.122 # pump out diffs from after the synch point
2025-07-01 03:00:15.130 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:15.142
2025-07-01 03:00:15.150 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:15.162 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:15.170
2025-07-01 03:00:15.182 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:15.197 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:15.206 alo = 82, ahi = 1101
2025-07-01 03:00:15.222 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:15.234 blo = 82, bhi = 1101
2025-07-01 03:00:15.246
2025-07-01 03:00:15.266 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:15.275 g = []
2025-07-01 03:00:15.290 if alo < ahi:
2025-07-01 03:00:15.306 if blo < bhi:
2025-07-01 03:00:15.319 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:15.330 else:
2025-07-01 03:00:15.342 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:15.349 elif blo < bhi:
2025-07-01 03:00:15.358 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:15.369
2025-07-01 03:00:15.378 >       yield from g
2025-07-01 03:00:15.389
2025-07-01 03:00:15.398 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:15.410 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:15.422
2025-07-01 03:00:15.438 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:15.450 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:15.462 alo = 82, ahi = 1101
2025-07-01 03:00:15.478 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:15.487 blo = 82, bhi = 1101
2025-07-01 03:00:15.500
2025-07-01 03:00:15.509 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:15.522 r"""
2025-07-01 03:00:15.530 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:15.542 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:15.554 synch point, and intraline difference marking is done on the
2025-07-01 03:00:15.564 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:15.574
2025-07-01 03:00:15.584 Example:
2025-07-01 03:00:15.598
2025-07-01 03:00:15.606 >>> d = Differ()
2025-07-01 03:00:15.618 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:15.626 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:15.638 >>> print(''.join(results), end="")
2025-07-01 03:00:15.650 - abcDefghiJkl
2025-07-01 03:00:15.670 + abcdefGhijkl
2025-07-01 03:00:15.702 """
2025-07-01 03:00:15.710
2025-07-01 03:00:15.726 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:15.742 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:15.750 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:15.758 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:15.772 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:15.790
2025-07-01 03:00:15.798 # search for the pair that matches best without being identical
2025-07-01 03:00:15.810 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:15.818 # on junk -- unless we have to)
2025-07-01 03:00:15.830 for j in range(blo, bhi):
2025-07-01 03:00:15.838 bj = b[j]
2025-07-01 03:00:15.850 cruncher.set_seq2(bj)
2025-07-01 03:00:15.858 for i in range(alo, ahi):
2025-07-01 03:00:15.866 ai = a[i]
2025-07-01 03:00:15.878 if ai == bj:
2025-07-01 03:00:15.886 if eqi is None:
2025-07-01 03:00:15.898 eqi, eqj = i, j
2025-07-01 03:00:15.910 continue
2025-07-01 03:00:15.922 cruncher.set_seq1(ai)
2025-07-01 03:00:15.934 # computing similarity is expensive, so use the quick
2025-07-01 03:00:15.942 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:15.954 # compares by a factor of 3.
2025-07-01 03:00:15.966 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:15.982 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:15.994 # of the computation is cached by cruncher
2025-07-01 03:00:16.002 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:16.018 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:16.026 cruncher.ratio() > best_ratio:
2025-07-01 03:00:16.038 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:16.050 if best_ratio < cutoff:
2025-07-01 03:00:16.058 # no non-identical "pretty close" pair
2025-07-01 03:00:16.070 if eqi is None:
2025-07-01 03:00:16.078 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:16.094 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:16.105 return
2025-07-01 03:00:16.118 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:16.130 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:16.146 else:
2025-07-01 03:00:16.166 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:16.181 eqi = None
2025-07-01 03:00:16.193
2025-07-01 03:00:16.203 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:16.217 # identical
2025-07-01 03:00:16.233
2025-07-01 03:00:16.249 # pump out diffs from before the synch point
2025-07-01 03:00:16.262 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:16.271
2025-07-01 03:00:16.277 # do intraline marking on the synch pair
2025-07-01 03:00:16.288 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:16.294 if eqi is None:
2025-07-01 03:00:16.302 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:16.312 atags = btags = ""
2025-07-01 03:00:16.330 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:16.342 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:16.350 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:16.366 if tag == 'replace':
2025-07-01 03:00:16.374 atags += '^' * la
2025-07-01 03:00:16.386 btags += '^' * lb
2025-07-01 03:00:16.394 elif tag == 'delete':
2025-07-01 03:00:16.406 atags += '-' * la
2025-07-01 03:00:16.426 elif tag == 'insert':
2025-07-01 03:00:16.437 btags += '+' * lb
2025-07-01 03:00:16.453 elif tag == 'equal':
2025-07-01 03:00:16.466 atags += ' ' * la
2025-07-01 03:00:16.480 btags += ' ' * lb
2025-07-01 03:00:16.490 else:
2025-07-01 03:00:16.501 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:16.514 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:16.526 else:
2025-07-01 03:00:16.538 # the synch pair is identical
2025-07-01 03:00:16.550 yield '  ' + aelt
2025-07-01 03:00:16.563
2025-07-01 03:00:16.574 # pump out diffs from after the synch point
2025-07-01 03:00:16.583 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:16.598
2025-07-01 03:00:16.606 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:16.622 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:16.638
2025-07-01 03:00:16.650 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:16.666 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:16.682 alo = 83, ahi = 1101
2025-07-01 03:00:16.690 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:16.702 blo = 83, bhi = 1101
2025-07-01 03:00:16.716
2025-07-01 03:00:16.726 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:16.746 g = []
2025-07-01 03:00:16.758 if alo < ahi:
2025-07-01 03:00:16.770 if blo < bhi:
2025-07-01 03:00:16.782 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:16.797 else:
2025-07-01 03:00:16.810 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:16.822 elif blo < bhi:
2025-07-01 03:00:16.834 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:16.842
2025-07-01 03:00:16.854 >       yield from g
2025-07-01 03:00:16.862
2025-07-01 03:00:16.874 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:16.882 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:16.894
2025-07-01 03:00:16.906 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:16.914 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:16.926 alo = 83, ahi = 1101
2025-07-01 03:00:16.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:16.946 blo = 83, bhi = 1101
2025-07-01 03:00:16.958
2025-07-01 03:00:16.966 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:16.978 r"""
2025-07-01 03:00:16.990 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:16.998 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:17.014 synch point, and intraline difference marking is done on the
2025-07-01 03:00:17.022 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:17.032
2025-07-01 03:00:17.048 Example:
2025-07-01 03:00:17.058
2025-07-01 03:00:17.075 >>> d = Differ()
2025-07-01 03:00:17.086 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:17.094 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:17.106 >>> print(''.join(results), end="")
2025-07-01 03:00:17.114 - abcDefghiJkl
2025-07-01 03:00:17.134 + abcdefGhijkl
2025-07-01 03:00:17.165 """
2025-07-01 03:00:17.181
2025-07-01 03:00:17.193 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:17.206 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:17.225 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:17.238 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:17.250 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:17.257
2025-07-01 03:00:17.268 # search for the pair that matches best without being identical
2025-07-01 03:00:17.286 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:17.298 # on junk -- unless we have to)
2025-07-01 03:00:17.310 for j in range(blo, bhi):
2025-07-01 03:00:17.318 bj = b[j]
2025-07-01 03:00:17.330 cruncher.set_seq2(bj)
2025-07-01 03:00:17.338 for i in range(alo, ahi):
2025-07-01 03:00:17.346 ai = a[i]
2025-07-01 03:00:17.358 if ai == bj:
2025-07-01 03:00:17.366 if eqi is None:
2025-07-01 03:00:17.378 eqi, eqj = i, j
2025-07-01 03:00:17.390 continue
2025-07-01 03:00:17.401 cruncher.set_seq1(ai)
2025-07-01 03:00:17.414 # computing similarity is expensive, so use the quick
2025-07-01 03:00:17.430 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:17.438 # compares by a factor of 3.
2025-07-01 03:00:17.450 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:17.466 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:17.478 # of the computation is cached by cruncher
2025-07-01 03:00:17.486 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:17.502 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:17.514 cruncher.ratio() > best_ratio:
2025-07-01 03:00:17.530 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:17.538 if best_ratio < cutoff:
2025-07-01 03:00:17.554 # no non-identical "pretty close" pair
2025-07-01 03:00:17.562 if eqi is None:
2025-07-01 03:00:17.578 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:17.586 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:17.598 return
2025-07-01 03:00:17.610 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:17.620 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:17.634 else:
2025-07-01 03:00:17.648 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:17.658 eqi = None
2025-07-01 03:00:17.674
2025-07-01 03:00:17.690 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:17.701 # identical
2025-07-01 03:00:17.714
2025-07-01 03:00:17.726 # pump out diffs from before the synch point
2025-07-01 03:00:17.742 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:17.750
2025-07-01 03:00:17.768 # do intraline marking on the synch pair
2025-07-01 03:00:17.778 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:17.790 if eqi is None:
2025-07-01 03:00:17.798 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:17.814 atags = btags = ""
2025-07-01 03:00:17.822 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:17.838 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:17.846 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:17.854 if tag == 'replace':
2025-07-01 03:00:17.866 atags += '^' * la
2025-07-01 03:00:17.874 btags += '^' * lb
2025-07-01 03:00:17.884 elif tag == 'delete':
2025-07-01 03:00:17.898 atags += '-' * la
2025-07-01 03:00:17.910 elif tag == 'insert':
2025-07-01 03:00:17.918 btags += '+' * lb
2025-07-01 03:00:17.926 elif tag == 'equal':
2025-07-01 03:00:17.936 atags += ' ' * la
2025-07-01 03:00:17.950 btags += ' ' * lb
2025-07-01 03:00:17.962 else:
2025-07-01 03:00:17.971 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:17.986 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:17.994 else:
2025-07-01 03:00:18.002 # the synch pair is identical
2025-07-01 03:00:18.014 yield '  ' + aelt
2025-07-01 03:00:18.026
2025-07-01 03:00:18.042 # pump out diffs from after the synch point
2025-07-01 03:00:18.052 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:18.070
2025-07-01 03:00:18.078 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:18.090 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:18.098
2025-07-01 03:00:18.113 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:18.126 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:18.134 alo = 84, ahi = 1101
2025-07-01 03:00:18.146 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:18.158 blo = 84, bhi = 1101
2025-07-01 03:00:18.170
2025-07-01 03:00:18.178 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:18.190 g = []
2025-07-01 03:00:18.198 if alo < ahi:
2025-07-01 03:00:18.214 if blo < bhi:
2025-07-01 03:00:18.229 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:18.245 else:
2025-07-01 03:00:18.261 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:18.272 elif blo < bhi:
2025-07-01 03:00:18.284 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:18.296
2025-07-01 03:00:18.309 >       yield from g
2025-07-01 03:00:18.322
2025-07-01 03:00:18.330 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:18.342 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:18.354
2025-07-01 03:00:18.366 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:18.378 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:18.394 alo = 84, ahi = 1101
2025-07-01 03:00:18.406 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:18.422 blo = 84, bhi = 1101
2025-07-01 03:00:18.436
2025-07-01 03:00:18.446 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:18.457 r"""
2025-07-01 03:00:18.470 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:18.479 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:18.494 synch point, and intraline difference marking is done on the
2025-07-01 03:00:18.506 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:18.520
2025-07-01 03:00:18.534 Example:
2025-07-01 03:00:18.550
2025-07-01 03:00:18.562 >>> d = Differ()
2025-07-01 03:00:18.585 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:18.594 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:18.606 >>> print(''.join(results), end="")
2025-07-01 03:00:18.618 - abcDefghiJkl
2025-07-01 03:00:18.634 + abcdefGhijkl
2025-07-01 03:00:18.650 """
2025-07-01 03:00:18.666
2025-07-01 03:00:18.673 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:18.686 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:18.699 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:18.711 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:18.721 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:18.737
2025-07-01 03:00:18.750 # search for the pair that matches best without being identical
2025-07-01 03:00:18.762 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:18.783 # on junk -- unless we have to)
2025-07-01 03:00:18.788 for j in range(blo, bhi):
2025-07-01 03:00:18.802 bj = b[j]
2025-07-01 03:00:18.810 cruncher.set_seq2(bj)
2025-07-01 03:00:18.822 for i in range(alo, ahi):
2025-07-01 03:00:18.834 ai = a[i]
2025-07-01 03:00:18.843 if ai == bj:
2025-07-01 03:00:18.858 if eqi is None:
2025-07-01 03:00:18.866 eqi, eqj = i, j
2025-07-01 03:00:18.878 continue
2025-07-01 03:00:18.890 cruncher.set_seq1(ai)
2025-07-01 03:00:18.898 # computing similarity is expensive, so use the quick
2025-07-01 03:00:18.910 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:18.922 # compares by a factor of 3.
2025-07-01 03:00:18.938 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:18.952 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:18.962 # of the computation is cached by cruncher
2025-07-01 03:00:18.978 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:18.995 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:19.001 cruncher.ratio() > best_ratio:
2025-07-01 03:00:19.018 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:19.026 if best_ratio < cutoff:
2025-07-01 03:00:19.038 # no non-identical "pretty close" pair
2025-07-01 03:00:19.050 if eqi is None:
2025-07-01 03:00:19.066 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:19.078 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:19.094 return
2025-07-01 03:00:19.102 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:19.113 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:19.124 else:
2025-07-01 03:00:19.138 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:19.153 eqi = None
2025-07-01 03:00:19.162
2025-07-01 03:00:19.174 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:19.181 # identical
2025-07-01 03:00:19.194
2025-07-01 03:00:19.202 # pump out diffs from before the synch point
2025-07-01 03:00:19.214 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:19.222
2025-07-01 03:00:19.230 # do intraline marking on the synch pair
2025-07-01 03:00:19.242 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:19.254 if eqi is None:
2025-07-01 03:00:19.266 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:19.278 atags = btags = ""
2025-07-01 03:00:19.290 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:19.298 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:19.310 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:19.318 if tag == 'replace':
2025-07-01 03:00:19.330 atags += '^' * la
2025-07-01 03:00:19.338 btags += '^' * lb
2025-07-01 03:00:19.350 elif tag == 'delete':
2025-07-01 03:00:19.362 atags += '-' * la
2025-07-01 03:00:19.370 elif tag == 'insert':
2025-07-01 03:00:19.382 btags += '+' * lb
2025-07-01 03:00:19.390 elif tag == 'equal':
2025-07-01 03:00:19.402 atags += ' ' * la
2025-07-01 03:00:19.412 btags += ' ' * lb
2025-07-01 03:00:19.426 else:
2025-07-01 03:00:19.434 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:19.446 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:19.454 else:
2025-07-01 03:00:19.469 # the synch pair is identical
2025-07-01 03:00:19.477 yield '  ' + aelt
2025-07-01 03:00:19.490
2025-07-01 03:00:19.498 # pump out diffs from after the synch point
2025-07-01 03:00:19.508 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:19.526
2025-07-01 03:00:19.538 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:19.554 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:19.562
2025-07-01 03:00:19.570 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:19.590 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:19.598 alo = 85, ahi = 1101
2025-07-01 03:00:19.614 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:19.630 blo = 85, bhi = 1101
2025-07-01 03:00:19.642
2025-07-01 03:00:19.658 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:19.674 g = []
2025-07-01 03:00:19.686 if alo < ahi:
2025-07-01 03:00:19.702 if blo < bhi:
2025-07-01 03:00:19.710 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:19.726 else:
2025-07-01 03:00:19.733 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:19.746 elif blo < bhi:
2025-07-01 03:00:19.762 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:19.774
2025-07-01 03:00:19.786 >       yield from g
2025-07-01 03:00:19.798
2025-07-01 03:00:19.810 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:19.818 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:19.830
2025-07-01 03:00:19.838 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:19.850 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:19.862 alo = 85, ahi = 1101
2025-07-01 03:00:19.874 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:19.886 blo = 85, bhi = 1101
2025-07-01 03:00:19.897
2025-07-01 03:00:19.906 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:19.921 r"""
2025-07-01 03:00:19.934 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:19.942 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:19.954 synch point, and intraline difference marking is done on the
2025-07-01 03:00:19.962 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:19.980
2025-07-01 03:00:19.990 Example:
2025-07-01 03:00:19.998
2025-07-01 03:00:20.010 >>> d = Differ()
2025-07-01 03:00:20.018 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:20.038 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:20.046 >>> print(''.join(results), end="")
2025-07-01 03:00:20.058 - abcDefghiJkl
2025-07-01 03:00:20.082 + abcdefGhijkl
2025-07-01 03:00:20.097 """
2025-07-01 03:00:20.110
2025-07-01 03:00:20.125 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:20.134 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:20.150 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:20.162 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:20.173 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:20.179
2025-07-01 03:00:20.194 # search for the pair that matches best without being identical
2025-07-01 03:00:20.206 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:20.222 # on junk -- unless we have to)
2025-07-01 03:00:20.234 for j in range(blo, bhi):
2025-07-01 03:00:20.242 bj = b[j]
2025-07-01 03:00:20.258 cruncher.set_seq2(bj)
2025-07-01 03:00:20.270 for i in range(alo, ahi):
2025-07-01 03:00:20.278 ai = a[i]
2025-07-01 03:00:20.290 if ai == bj:
2025-07-01 03:00:20.298 if eqi is None:
2025-07-01 03:00:20.308 eqi, eqj = i, j
2025-07-01 03:00:20.322 continue
2025-07-01 03:00:20.338 cruncher.set_seq1(ai)
2025-07-01 03:00:20.346 # computing similarity is expensive, so use the quick
2025-07-01 03:00:20.354 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:20.370 # compares by a factor of 3.
2025-07-01 03:00:20.378 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:20.390 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:20.398 # of the computation is cached by cruncher
2025-07-01 03:00:20.408 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:20.421 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:20.434 cruncher.ratio() > best_ratio:
2025-07-01 03:00:20.446 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:20.458 if best_ratio < cutoff:
2025-07-01 03:00:20.470 # no non-identical "pretty close" pair
2025-07-01 03:00:20.478 if eqi is None:
2025-07-01 03:00:20.490 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:20.506 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:20.522 return
2025-07-01 03:00:20.542 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:20.549 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:20.559 else:
2025-07-01 03:00:20.574 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:20.586 eqi = None
2025-07-01 03:00:20.594
2025-07-01 03:00:20.606 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:20.614 # identical
2025-07-01 03:00:20.626
2025-07-01 03:00:20.640 # pump out diffs from before the synch point
2025-07-01 03:00:20.654 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:20.664
2025-07-01 03:00:20.674 # do intraline marking on the synch pair
2025-07-01 03:00:20.686 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:20.698 if eqi is None:
2025-07-01 03:00:20.713 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:20.726 atags = btags = ""
2025-07-01 03:00:20.738 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:20.750 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:20.762 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:20.778 if tag == 'replace':
2025-07-01 03:00:20.790 atags += '^' * la
2025-07-01 03:00:20.798 btags += '^' * lb
2025-07-01 03:00:20.809 elif tag == 'delete':
2025-07-01 03:00:20.818 atags += '-' * la
2025-07-01 03:00:20.830 elif tag == 'insert':
2025-07-01 03:00:20.842 btags += '+' * lb
2025-07-01 03:00:20.850 elif tag == 'equal':
2025-07-01 03:00:20.862 atags += ' ' * la
2025-07-01 03:00:20.870 btags += ' ' * lb
2025-07-01 03:00:20.886 else:
2025-07-01 03:00:20.894 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:20.909 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:20.918 else:
2025-07-01 03:00:20.937 # the synch pair is identical
2025-07-01 03:00:20.946 yield '  ' + aelt
2025-07-01 03:00:20.954
2025-07-01 03:00:20.966 # pump out diffs from after the synch point
2025-07-01 03:00:20.978 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:20.986
2025-07-01 03:00:21.010 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:21.017 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:21.030
2025-07-01 03:00:21.043 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:21.053 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:21.066 alo = 86, ahi = 1101
2025-07-01 03:00:21.082 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:21.098 blo = 86, bhi = 1101
2025-07-01 03:00:21.133
2025-07-01 03:00:21.154 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:21.178 g = []
2025-07-01 03:00:21.185 if alo < ahi:
2025-07-01 03:00:21.191 if blo < bhi:
2025-07-01 03:00:21.198 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:21.203 else:
2025-07-01 03:00:21.208 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:21.212 elif blo < bhi:
2025-07-01 03:00:21.217 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:21.222
2025-07-01 03:00:21.226 >       yield from g
2025-07-01 03:00:21.230
2025-07-01 03:00:21.237 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:21.243 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:21.247
2025-07-01 03:00:21.252 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:21.257 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:21.261 alo = 86, ahi = 1101
2025-07-01 03:00:21.267 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:21.271 blo = 86, bhi = 1101
2025-07-01 03:00:21.276
2025-07-01 03:00:21.280 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:21.285 r"""
2025-07-01 03:00:21.289 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:21.294 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:21.298 synch point, and intraline difference marking is done on the
2025-07-01 03:00:21.303 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:21.307
2025-07-01 03:00:21.312 Example:
2025-07-01 03:00:21.317
2025-07-01 03:00:21.322 >>> d = Differ()
2025-07-01 03:00:21.326 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:21.331 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:21.335 >>> print(''.join(results), end="")
2025-07-01 03:00:21.341 - abcDefghiJkl
2025-07-01 03:00:21.349 + abcdefGhijkl
2025-07-01 03:00:21.358 """
2025-07-01 03:00:21.363
2025-07-01 03:00:21.368 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:21.373 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:21.377 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:21.382 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:21.387 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:21.393
2025-07-01 03:00:21.399 # search for the pair that matches best without being identical
2025-07-01 03:00:21.404 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:21.408 # on junk -- unless we have to)
2025-07-01 03:00:21.413 for j in range(blo, bhi):
2025-07-01 03:00:21.417 bj = b[j]
2025-07-01 03:00:21.421 cruncher.set_seq2(bj)
2025-07-01 03:00:21.426 for i in range(alo, ahi):
2025-07-01 03:00:21.430 ai = a[i]
2025-07-01 03:00:21.434 if ai == bj:
2025-07-01 03:00:21.439 if eqi is None:
2025-07-01 03:00:21.444 eqi, eqj = i, j
2025-07-01 03:00:21.448 continue
2025-07-01 03:00:21.453 cruncher.set_seq1(ai)
2025-07-01 03:00:21.458 # computing similarity is expensive, so use the quick
2025-07-01 03:00:21.463 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:21.467 # compares by a factor of 3.
2025-07-01 03:00:21.472 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:21.478 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:21.483 # of the computation is cached by cruncher
2025-07-01 03:00:21.488 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:21.493 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:21.498 cruncher.ratio() > best_ratio:
2025-07-01 03:00:21.503 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:21.507 if best_ratio < cutoff:
2025-07-01 03:00:21.512 # no non-identical "pretty close" pair
2025-07-01 03:00:21.516 if eqi is None:
2025-07-01 03:00:21.521 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:21.526 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:21.531 return
2025-07-01 03:00:21.535 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:21.540 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:21.545 else:
2025-07-01 03:00:21.549 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:21.554 eqi = None
2025-07-01 03:00:21.558
2025-07-01 03:00:21.563 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:21.568 # identical
2025-07-01 03:00:21.572
2025-07-01 03:00:21.577 # pump out diffs from before the synch point
2025-07-01 03:00:21.582 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:21.586
2025-07-01 03:00:21.591 # do intraline marking on the synch pair
2025-07-01 03:00:21.596 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:21.600 if eqi is None:
2025-07-01 03:00:21.605 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:21.609 atags = btags = ""
2025-07-01 03:00:21.614 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:21.618 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:21.623 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:21.627 if tag == 'replace':
2025-07-01 03:00:21.632 atags += '^' * la
2025-07-01 03:00:21.636 btags += '^' * lb
2025-07-01 03:00:21.641 elif tag == 'delete':
2025-07-01 03:00:21.646 atags += '-' * la
2025-07-01 03:00:21.652 elif tag == 'insert':
2025-07-01 03:00:21.657 btags += '+' * lb
2025-07-01 03:00:21.662 elif tag == 'equal':
2025-07-01 03:00:21.666 atags += ' ' * la
2025-07-01 03:00:21.671 btags += ' ' * lb
2025-07-01 03:00:21.675 else:
2025-07-01 03:00:21.680 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:21.684 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:21.689 else:
2025-07-01 03:00:21.694 # the synch pair is identical
2025-07-01 03:00:21.699 yield '  ' + aelt
2025-07-01 03:00:21.703
2025-07-01 03:00:21.708 # pump out diffs from after the synch point
2025-07-01 03:00:21.712 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:21.717
2025-07-01 03:00:21.721 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:21.726 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:21.730
2025-07-01 03:00:21.735 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:21.740 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:21.744 alo = 87, ahi = 1101
2025-07-01 03:00:21.749 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:21.754 blo = 87, bhi = 1101
2025-07-01 03:00:21.758
2025-07-01 03:00:21.762 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:21.767 g = []
2025-07-01 03:00:21.772 if alo < ahi:
2025-07-01 03:00:21.776 if blo < bhi:
2025-07-01 03:00:21.781 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:21.785 else:
2025-07-01 03:00:21.791 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:21.795 elif blo < bhi:
2025-07-01 03:00:21.800 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:21.805
2025-07-01 03:00:21.809 >       yield from g
2025-07-01 03:00:21.813
2025-07-01 03:00:21.818 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:21.822 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:21.827
2025-07-01 03:00:21.832 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:21.836 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:21.841 alo = 87, ahi = 1101
2025-07-01 03:00:21.846 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:21.850 blo = 87, bhi = 1101
2025-07-01 03:00:21.854
2025-07-01 03:00:21.859 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:21.863 r"""
2025-07-01 03:00:21.867 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:21.872 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:21.877 synch point, and intraline difference marking is done on the
2025-07-01 03:00:21.881 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:21.886
2025-07-01 03:00:21.890 Example:
2025-07-01 03:00:21.895
2025-07-01 03:00:21.899 >>> d = Differ()
2025-07-01 03:00:21.904 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:21.909 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:21.913 >>> print(''.join(results), end="")
2025-07-01 03:00:21.918 - abcDefghiJkl
2025-07-01 03:00:21.927 + abcdefGhijkl
2025-07-01 03:00:21.946 """
2025-07-01 03:00:21.962
2025-07-01 03:00:21.970 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:21.978 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:21.994 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:22.010 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:22.018 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:22.034
2025-07-01 03:00:22.049 # search for the pair that matches best without being identical
2025-07-01 03:00:22.062 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:22.071 # on junk -- unless we have to)
2025-07-01 03:00:22.086 for j in range(blo, bhi):
2025-07-01 03:00:22.098 bj = b[j]
2025-07-01 03:00:22.114 cruncher.set_seq2(bj)
2025-07-01 03:00:22.122 for i in range(alo, ahi):
2025-07-01 03:00:22.134 ai = a[i]
2025-07-01 03:00:22.146 if ai == bj:
2025-07-01 03:00:22.156 if eqi is None:
2025-07-01 03:00:22.170 eqi, eqj = i, j
2025-07-01 03:00:22.186 continue
2025-07-01 03:00:22.198 cruncher.set_seq1(ai)
2025-07-01 03:00:22.206 # computing similarity is expensive, so use the quick
2025-07-01 03:00:22.218 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:22.230 # compares by a factor of 3.
2025-07-01 03:00:22.238 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:22.246 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:22.258 # of the computation is cached by cruncher
2025-07-01 03:00:22.270 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:22.282 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:22.294 cruncher.ratio() > best_ratio:
2025-07-01 03:00:22.302 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:22.312 if best_ratio < cutoff:
2025-07-01 03:00:22.322 # no non-identical "pretty close" pair
2025-07-01 03:00:22.334 if eqi is None:
2025-07-01 03:00:22.342 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:22.352 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:22.366 return
2025-07-01 03:00:22.378 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:22.386 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:22.398 else:
2025-07-01 03:00:22.410 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:22.418 eqi = None
2025-07-01 03:00:22.427
2025-07-01 03:00:22.442 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:22.454 # identical
2025-07-01 03:00:22.466
2025-07-01 03:00:22.481 # pump out diffs from before the synch point
2025-07-01 03:00:22.494 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:22.499
2025-07-01 03:00:22.505 # do intraline marking on the synch pair
2025-07-01 03:00:22.512 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:22.519 if eqi is None:
2025-07-01 03:00:22.526 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:22.532 atags = btags = ""
2025-07-01 03:00:22.537 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:22.543 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:22.549 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:22.555 if tag == 'replace':
2025-07-01 03:00:22.561 atags += '^' * la
2025-07-01 03:00:22.567 btags += '^' * lb
2025-07-01 03:00:22.574 elif tag == 'delete':
2025-07-01 03:00:22.578 atags += '-' * la
2025-07-01 03:00:22.585 elif tag == 'insert':
2025-07-01 03:00:22.592 btags += '+' * lb
2025-07-01 03:00:22.599 elif tag == 'equal':
2025-07-01 03:00:22.606 atags += ' ' * la
2025-07-01 03:00:22.613 btags += ' ' * lb
2025-07-01 03:00:22.618 else:
2025-07-01 03:00:22.624 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:22.631 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:22.638 else:
2025-07-01 03:00:22.643 # the synch pair is identical
2025-07-01 03:00:22.649 yield '  ' + aelt
2025-07-01 03:00:22.657
2025-07-01 03:00:22.663 # pump out diffs from after the synch point
2025-07-01 03:00:22.669 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:22.683
2025-07-01 03:00:22.698 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:22.709 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:22.713
2025-07-01 03:00:22.718 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:22.724 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:22.729 alo = 88, ahi = 1101
2025-07-01 03:00:22.735 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:22.740 blo = 88, bhi = 1101
2025-07-01 03:00:22.750
2025-07-01 03:00:22.766 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:22.778 g = []
2025-07-01 03:00:22.786 if alo < ahi:
2025-07-01 03:00:22.798 if blo < bhi:
2025-07-01 03:00:22.806 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:22.818 else:
2025-07-01 03:00:22.830 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:22.838 elif blo < bhi:
2025-07-01 03:00:22.850 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:22.858
2025-07-01 03:00:22.866 >       yield from g
2025-07-01 03:00:22.878
2025-07-01 03:00:22.894 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:22.905 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:22.918
2025-07-01 03:00:22.933 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:22.949 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:22.965 alo = 88, ahi = 1101
2025-07-01 03:00:22.974 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:22.990 blo = 88, bhi = 1101
2025-07-01 03:00:22.998
2025-07-01 03:00:23.009 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:23.017 r"""
2025-07-01 03:00:23.025 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:23.037 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:23.049 synch point, and intraline difference marking is done on the
2025-07-01 03:00:23.062 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:23.078
2025-07-01 03:00:23.096 Example:
2025-07-01 03:00:23.106
2025-07-01 03:00:23.121 >>> d = Differ()
2025-07-01 03:00:23.133 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:23.148 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:23.165 >>> print(''.join(results), end="")
2025-07-01 03:00:23.178 - abcDefghiJkl
2025-07-01 03:00:23.202 + abcdefGhijkl
2025-07-01 03:00:23.222 """
2025-07-01 03:00:23.229
2025-07-01 03:00:23.242 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:23.249 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:23.262 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:23.270 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:23.281 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:23.289
2025-07-01 03:00:23.306 # search for the pair that matches best without being identical
2025-07-01 03:00:23.320 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:23.330 # on junk -- unless we have to)
2025-07-01 03:00:23.342 for j in range(blo, bhi):
2025-07-01 03:00:23.354 bj = b[j]
2025-07-01 03:00:23.362 cruncher.set_seq2(bj)
2025-07-01 03:00:23.374 for i in range(alo, ahi):
2025-07-01 03:00:23.381 ai = a[i]
2025-07-01 03:00:23.394 if ai == bj:
2025-07-01 03:00:23.406 if eqi is None:
2025-07-01 03:00:23.416 eqi, eqj = i, j
2025-07-01 03:00:23.430 continue
2025-07-01 03:00:23.438 cruncher.set_seq1(ai)
2025-07-01 03:00:23.446 # computing similarity is expensive, so use the quick
2025-07-01 03:00:23.458 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:23.470 # compares by a factor of 3.
2025-07-01 03:00:23.482 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:23.489 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:23.504 # of the computation is cached by cruncher
2025-07-01 03:00:23.516 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:23.525 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:23.540 cruncher.ratio() > best_ratio:
2025-07-01 03:00:23.549 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:23.562 if best_ratio < cutoff:
2025-07-01 03:00:23.575 # no non-identical "pretty close" pair
2025-07-01 03:00:23.588 if eqi is None:
2025-07-01 03:00:23.600 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:23.616 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:23.629 return
2025-07-01 03:00:23.642 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:23.650 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:23.657 else:
2025-07-01 03:00:23.670 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:23.678 eqi = None
2025-07-01 03:00:23.690
2025-07-01 03:00:23.697 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:23.710 # identical
2025-07-01 03:00:23.718
2025-07-01 03:00:23.730 # pump out diffs from before the synch point
2025-07-01 03:00:23.737 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:23.750
2025-07-01 03:00:23.758 # do intraline marking on the synch pair
2025-07-01 03:00:23.770 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:23.777 if eqi is None:
2025-07-01 03:00:23.790 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:23.798 atags = btags = ""
2025-07-01 03:00:23.810 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:23.826 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:23.837 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:23.846 if tag == 'replace':
2025-07-01 03:00:23.858 atags += '^' * la
2025-07-01 03:00:23.866 btags += '^' * lb
2025-07-01 03:00:23.878 elif tag == 'delete':
2025-07-01 03:00:23.893 atags += '-' * la
2025-07-01 03:00:23.903 elif tag == 'insert':
2025-07-01 03:00:23.910 btags += '+' * lb
2025-07-01 03:00:23.922 elif tag == 'equal':
2025-07-01 03:00:23.929 atags += ' ' * la
2025-07-01 03:00:23.934 btags += ' ' * lb
2025-07-01 03:00:23.940 else:
2025-07-01 03:00:23.946 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:23.954 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:23.962 else:
2025-07-01 03:00:23.969 # the synch pair is identical
2025-07-01 03:00:23.974 yield '  ' + aelt
2025-07-01 03:00:23.985
2025-07-01 03:00:23.990 # pump out diffs from after the synch point
2025-07-01 03:00:24.002 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:24.018
2025-07-01 03:00:24.026 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:24.038 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:24.050
2025-07-01 03:00:24.062 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:24.079 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:24.090 alo = 89, ahi = 1101
2025-07-01 03:00:24.106 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:24.118 blo = 89, bhi = 1101
2025-07-01 03:00:24.130
2025-07-01 03:00:24.138 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:24.150 g = []
2025-07-01 03:00:24.158 if alo < ahi:
2025-07-01 03:00:24.170 if blo < bhi:
2025-07-01 03:00:24.186 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:24.194 else:
2025-07-01 03:00:24.206 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:24.222 elif blo < bhi:
2025-07-01 03:00:24.230 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:24.238
2025-07-01 03:00:24.246 >       yield from g
2025-07-01 03:00:24.258
2025-07-01 03:00:24.266 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:24.278 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:24.290
2025-07-01 03:00:24.295 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:24.310 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:24.322 alo = 89, ahi = 1101
2025-07-01 03:00:24.330 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:24.342 blo = 89, bhi = 1101
2025-07-01 03:00:24.350
2025-07-01 03:00:24.357 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:24.366 r"""
2025-07-01 03:00:24.376 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:24.390 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:24.402 synch point, and intraline difference marking is done on the
2025-07-01 03:00:24.412 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:24.426
2025-07-01 03:00:24.442 Example:
2025-07-01 03:00:24.450
2025-07-01 03:00:24.458 >>> d = Differ()
2025-07-01 03:00:24.470 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:24.482 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:24.494 >>> print(''.join(results), end="")
2025-07-01 03:00:24.502 - abcDefghiJkl
2025-07-01 03:00:24.530 + abcdefGhijkl
2025-07-01 03:00:24.550 """
2025-07-01 03:00:24.558
2025-07-01 03:00:24.573 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:24.582 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:24.587 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:24.593 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:24.601 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:24.617
2025-07-01 03:00:24.622 # search for the pair that matches best without being identical
2025-07-01 03:00:24.633 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:24.647 # on junk -- unless we have to)
2025-07-01 03:00:24.657 for j in range(blo, bhi):
2025-07-01 03:00:24.666 bj = b[j]
2025-07-01 03:00:24.678 cruncher.set_seq2(bj)
2025-07-01 03:00:24.690 for i in range(alo, ahi):
2025-07-01 03:00:24.702 ai = a[i]
2025-07-01 03:00:24.710 if ai == bj:
2025-07-01 03:00:24.722 if eqi is None:
2025-07-01 03:00:24.733 eqi, eqj = i, j
2025-07-01 03:00:24.746 continue
2025-07-01 03:00:24.762 cruncher.set_seq1(ai)
2025-07-01 03:00:24.770 # computing similarity is expensive, so use the quick
2025-07-01 03:00:24.778 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:24.790 # compares by a factor of 3.
2025-07-01 03:00:24.802 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:24.814 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:24.825 # of the computation is cached by cruncher
2025-07-01 03:00:24.838 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:24.846 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:24.857 cruncher.ratio() > best_ratio:
2025-07-01 03:00:24.874 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:24.886 if best_ratio < cutoff:
2025-07-01 03:00:24.898 # no non-identical "pretty close" pair
2025-07-01 03:00:24.914 if eqi is None:
2025-07-01 03:00:24.922 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:24.933 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:24.950 return
2025-07-01 03:00:24.962 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:24.974 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:24.982 else:
2025-07-01 03:00:24.996 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:25.010 eqi = None
2025-07-01 03:00:25.018
2025-07-01 03:00:25.030 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:25.046 # identical
2025-07-01 03:00:25.054
2025-07-01 03:00:25.066 # pump out diffs from before the synch point
2025-07-01 03:00:25.078 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:25.094
2025-07-01 03:00:25.102 # do intraline marking on the synch pair
2025-07-01 03:00:25.110 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:25.122 if eqi is None:
2025-07-01 03:00:25.130 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:25.142 atags = btags = ""
2025-07-01 03:00:25.150 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:25.162 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:25.170 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:25.182 if tag == 'replace':
2025-07-01 03:00:25.190 atags += '^' * la
2025-07-01 03:00:25.202 btags += '^' * lb
2025-07-01 03:00:25.210 elif tag == 'delete':
2025-07-01 03:00:25.222 atags += '-' * la
2025-07-01 03:00:25.230 elif tag == 'insert':
2025-07-01 03:00:25.242 btags += '+' * lb
2025-07-01 03:00:25.250 elif tag == 'equal':
2025-07-01 03:00:25.262 atags += ' ' * la
2025-07-01 03:00:25.270 btags += ' ' * lb
2025-07-01 03:00:25.282 else:
2025-07-01 03:00:25.290 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:25.302 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:25.310 else:
2025-07-01 03:00:25.322 # the synch pair is identical
2025-07-01 03:00:25.329 yield '  ' + aelt
2025-07-01 03:00:25.346
2025-07-01 03:00:25.354 # pump out diffs from after the synch point
2025-07-01 03:00:25.366 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:25.374
2025-07-01 03:00:25.383 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:25.392 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:25.410
2025-07-01 03:00:25.422 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:25.431 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:25.438 alo = 92, ahi = 1101
2025-07-01 03:00:25.451 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:25.465 blo = 92, bhi = 1101
2025-07-01 03:00:25.476
2025-07-01 03:00:25.486 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:25.495 g = []
2025-07-01 03:00:25.510 if alo < ahi:
2025-07-01 03:00:25.525 if blo < bhi:
2025-07-01 03:00:25.530 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:25.545 else:
2025-07-01 03:00:25.559 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:25.570 elif blo < bhi:
2025-07-01 03:00:25.582 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:25.594
2025-07-01 03:00:25.611 >       yield from g
2025-07-01 03:00:25.622
2025-07-01 03:00:25.629 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:25.642 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:25.658
2025-07-01 03:00:25.669 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:25.686 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:25.694 alo = 92, ahi = 1101
2025-07-01 03:00:25.706 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:25.714 blo = 92, bhi = 1101
2025-07-01 03:00:25.726
2025-07-01 03:00:25.734 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:25.746 r"""
2025-07-01 03:00:25.754 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:25.766 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:25.778 synch point, and intraline difference marking is done on the
2025-07-01 03:00:25.786 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:25.800
2025-07-01 03:00:25.814 Example:
2025-07-01 03:00:25.826
2025-07-01 03:00:25.834 >>> d = Differ()
2025-07-01 03:00:25.846 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:25.858 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:25.866 >>> print(''.join(results), end="")
2025-07-01 03:00:25.878 - abcDefghiJkl
2025-07-01 03:00:25.902 + abcdefGhijkl
2025-07-01 03:00:25.918 """
2025-07-01 03:00:25.930
2025-07-01 03:00:25.938 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:25.952 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:25.966 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:25.974 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:25.986 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:25.996
2025-07-01 03:00:26.006 # search for the pair that matches best without being identical
2025-07-01 03:00:26.018 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:26.034 # on junk -- unless we have to)
2025-07-01 03:00:26.042 for j in range(blo, bhi):
2025-07-01 03:00:26.050 bj = b[j]
2025-07-01 03:00:26.062 cruncher.set_seq2(bj)
2025-07-01 03:00:26.074 for i in range(alo, ahi):
2025-07-01 03:00:26.082 ai = a[i]
2025-07-01 03:00:26.088 if ai == bj:
2025-07-01 03:00:26.093 if eqi is None:
2025-07-01 03:00:26.097 eqi, eqj = i, j
2025-07-01 03:00:26.102 continue
2025-07-01 03:00:26.106 cruncher.set_seq1(ai)
2025-07-01 03:00:26.111 # computing similarity is expensive, so use the quick
2025-07-01 03:00:26.116 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:26.122 # compares by a factor of 3.
2025-07-01 03:00:26.127 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:26.133 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:26.139 # of the computation is cached by cruncher
2025-07-01 03:00:26.144 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:26.149 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:26.155 cruncher.ratio() > best_ratio:
2025-07-01 03:00:26.160 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:26.165 if best_ratio < cutoff:
2025-07-01 03:00:26.171 # no non-identical "pretty close" pair
2025-07-01 03:00:26.176 if eqi is None:
2025-07-01 03:00:26.182 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:26.187 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:26.191 return
2025-07-01 03:00:26.196 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:26.201 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:26.206 else:
2025-07-01 03:00:26.210 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:26.215 eqi = None
2025-07-01 03:00:26.219
2025-07-01 03:00:26.224 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:26.228 # identical
2025-07-01 03:00:26.232
2025-07-01 03:00:26.237 # pump out diffs from before the synch point
2025-07-01 03:00:26.241 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:26.246
2025-07-01 03:00:26.250 # do intraline marking on the synch pair
2025-07-01 03:00:26.255 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:26.259 if eqi is None:
2025-07-01 03:00:26.264 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:26.269 atags = btags = ""
2025-07-01 03:00:26.273 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:26.278 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:26.283 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:26.288 if tag == 'replace':
2025-07-01 03:00:26.292 atags += '^' * la
2025-07-01 03:00:26.297 btags += '^' * lb
2025-07-01 03:00:26.301 elif tag == 'delete':
2025-07-01 03:00:26.306 atags += '-' * la
2025-07-01 03:00:26.310 elif tag == 'insert':
2025-07-01 03:00:26.315 btags += '+' * lb
2025-07-01 03:00:26.319 elif tag == 'equal':
2025-07-01 03:00:26.323 atags += ' ' * la
2025-07-01 03:00:26.328 btags += ' ' * lb
2025-07-01 03:00:26.333 else:
2025-07-01 03:00:26.337 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:26.342 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:26.347 else:
2025-07-01 03:00:26.352 # the synch pair is identical
2025-07-01 03:00:26.356 yield '  ' + aelt
2025-07-01 03:00:26.361
2025-07-01 03:00:26.365 # pump out diffs from after the synch point
2025-07-01 03:00:26.370 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:26.374
2025-07-01 03:00:26.379 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:26.383 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:26.388
2025-07-01 03:00:26.392 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:26.397 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:26.401 alo = 93, ahi = 1101
2025-07-01 03:00:26.406 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:26.410 blo = 93, bhi = 1101
2025-07-01 03:00:26.415
2025-07-01 03:00:26.419 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:26.423 g = []
2025-07-01 03:00:26.428 if alo < ahi:
2025-07-01 03:00:26.432 if blo < bhi:
2025-07-01 03:00:26.437 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:26.442 else:
2025-07-01 03:00:26.446 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:26.451 elif blo < bhi:
2025-07-01 03:00:26.455 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:26.460
2025-07-01 03:00:26.464 >       yield from g
2025-07-01 03:00:26.468
2025-07-01 03:00:26.472 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:26.477 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:26.481
2025-07-01 03:00:26.487 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:26.491 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:26.496 alo = 93, ahi = 1101
2025-07-01 03:00:26.501 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:26.505 blo = 93, bhi = 1101
2025-07-01 03:00:26.509
2025-07-01 03:00:26.514 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:26.518 r"""
2025-07-01 03:00:26.523 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:26.528 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:26.532 synch point, and intraline difference marking is done on the
2025-07-01 03:00:26.537 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:26.542
2025-07-01 03:00:26.546 Example:
2025-07-01 03:00:26.551
2025-07-01 03:00:26.556 >>> d = Differ()
2025-07-01 03:00:26.561 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:26.565 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:26.570 >>> print(''.join(results), end="")
2025-07-01 03:00:26.575 - abcDefghiJkl
2025-07-01 03:00:26.585 + abcdefGhijkl
2025-07-01 03:00:26.595 """
2025-07-01 03:00:26.600
2025-07-01 03:00:26.605 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:26.610 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:26.614 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:26.619 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:26.624 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:26.628
2025-07-01 03:00:26.633 # search for the pair that matches best without being identical
2025-07-01 03:00:26.637 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:26.642 # on junk -- unless we have to)
2025-07-01 03:00:26.647 for j in range(blo, bhi):
2025-07-01 03:00:26.651 bj = b[j]
2025-07-01 03:00:26.656 cruncher.set_seq2(bj)
2025-07-01 03:00:26.660 for i in range(alo, ahi):
2025-07-01 03:00:26.665 ai = a[i]
2025-07-01 03:00:26.670 if ai == bj:
2025-07-01 03:00:26.674 if eqi is None:
2025-07-01 03:00:26.679 eqi, eqj = i, j
2025-07-01 03:00:26.683 continue
2025-07-01 03:00:26.688 cruncher.set_seq1(ai)
2025-07-01 03:00:26.693 # computing similarity is expensive, so use the quick
2025-07-01 03:00:26.697 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:26.701 # compares by a factor of 3.
2025-07-01 03:00:26.706 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:26.710 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:26.716 # of the computation is cached by cruncher
2025-07-01 03:00:26.720 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:26.725 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:26.730 cruncher.ratio() > best_ratio:
2025-07-01 03:00:26.735 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:26.739 if best_ratio < cutoff:
2025-07-01 03:00:26.744 # no non-identical "pretty close" pair
2025-07-01 03:00:26.749 if eqi is None:
2025-07-01 03:00:26.754 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:26.759 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:26.763 return
2025-07-01 03:00:26.768 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:26.773 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:26.777 else:
2025-07-01 03:00:26.782 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:26.786 eqi = None
2025-07-01 03:00:26.791
2025-07-01 03:00:26.795 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:26.800 # identical
2025-07-01 03:00:26.804
2025-07-01 03:00:26.809 # pump out diffs from before the synch point
2025-07-01 03:00:26.813 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:26.818
2025-07-01 03:00:26.822 # do intraline marking on the synch pair
2025-07-01 03:00:26.827 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:26.831 if eqi is None:
2025-07-01 03:00:26.836 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:26.840 atags = btags = ""
2025-07-01 03:00:26.845 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:26.851 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:26.857 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:26.862 if tag == 'replace':
2025-07-01 03:00:26.867 atags += '^' * la
2025-07-01 03:00:26.873 btags += '^' * lb
2025-07-01 03:00:26.878 elif tag == 'delete':
2025-07-01 03:00:26.883 atags += '-' * la
2025-07-01 03:00:26.889 elif tag == 'insert':
2025-07-01 03:00:26.893 btags += '+' * lb
2025-07-01 03:00:26.899 elif tag == 'equal':
2025-07-01 03:00:26.904 atags += ' ' * la
2025-07-01 03:00:26.910 btags += ' ' * lb
2025-07-01 03:00:26.915 else:
2025-07-01 03:00:26.921 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:26.927 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:26.933 else:
2025-07-01 03:00:26.939 # the synch pair is identical
2025-07-01 03:00:26.946 yield '  ' + aelt
2025-07-01 03:00:26.952
2025-07-01 03:00:26.959 # pump out diffs from after the synch point
2025-07-01 03:00:26.966 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:26.972
2025-07-01 03:00:26.977 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:26.982 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:26.987
2025-07-01 03:00:26.992 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:26.997 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:27.002 alo = 94, ahi = 1101
2025-07-01 03:00:27.007 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:27.012 blo = 94, bhi = 1101
2025-07-01 03:00:27.016
2025-07-01 03:00:27.020 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:27.025 g = []
2025-07-01 03:00:27.029 if alo < ahi:
2025-07-01 03:00:27.033 if blo < bhi:
2025-07-01 03:00:27.038 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:27.043 else:
2025-07-01 03:00:27.047 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:27.051 elif blo < bhi:
2025-07-01 03:00:27.056 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:27.060
2025-07-01 03:00:27.064 >       yield from g
2025-07-01 03:00:27.069
2025-07-01 03:00:27.073 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:27.078 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:27.082
2025-07-01 03:00:27.087 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:27.091 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:27.095 alo = 94, ahi = 1101
2025-07-01 03:00:27.100 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:27.105 blo = 94, bhi = 1101
2025-07-01 03:00:27.109
2025-07-01 03:00:27.113 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:27.118 r"""
2025-07-01 03:00:27.122 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:27.127 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:27.131 synch point, and intraline difference marking is done on the
2025-07-01 03:00:27.136 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:27.140
2025-07-01 03:00:27.144 Example:
2025-07-01 03:00:27.148
2025-07-01 03:00:27.153 >>> d = Differ()
2025-07-01 03:00:27.157 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:27.161 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:27.166 >>> print(''.join(results), end="")
2025-07-01 03:00:27.171 - abcDefghiJkl
2025-07-01 03:00:27.180 + abcdefGhijkl
2025-07-01 03:00:27.188 """
2025-07-01 03:00:27.193
2025-07-01 03:00:27.197 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:27.202 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:27.206 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:27.210 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:27.215 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:27.219
2025-07-01 03:00:27.224 # search for the pair that matches best without being identical
2025-07-01 03:00:27.228 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:27.232 # on junk -- unless we have to)
2025-07-01 03:00:27.237 for j in range(blo, bhi):
2025-07-01 03:00:27.241 bj = b[j]
2025-07-01 03:00:27.246 cruncher.set_seq2(bj)
2025-07-01 03:00:27.251 for i in range(alo, ahi):
2025-07-01 03:00:27.266 ai = a[i]
2025-07-01 03:00:27.278 if ai == bj:
2025-07-01 03:00:27.285 if eqi is None:
2025-07-01 03:00:27.294 eqi, eqj = i, j
2025-07-01 03:00:27.306 continue
2025-07-01 03:00:27.314 cruncher.set_seq1(ai)
2025-07-01 03:00:27.326 # computing similarity is expensive, so use the quick
2025-07-01 03:00:27.337 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:27.350 # compares by a factor of 3.
2025-07-01 03:00:27.358 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:27.370 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:27.378 # of the computation is cached by cruncher
2025-07-01 03:00:27.390 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:27.400 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:27.415 cruncher.ratio() > best_ratio:
2025-07-01 03:00:27.430 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:27.438 if best_ratio < cutoff:
2025-07-01 03:00:27.450 # no non-identical "pretty close" pair
2025-07-01 03:00:27.462 if eqi is None:
2025-07-01 03:00:27.474 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:27.486 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:27.495 return
2025-07-01 03:00:27.510 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:27.526 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:27.534 else:
2025-07-01 03:00:27.556 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:27.566 eqi = None
2025-07-01 03:00:27.575
2025-07-01 03:00:27.583 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:27.601 # identical
2025-07-01 03:00:27.610
2025-07-01 03:00:27.622 # pump out diffs from before the synch point
2025-07-01 03:00:27.631 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:27.646
2025-07-01 03:00:27.657 # do intraline marking on the synch pair
2025-07-01 03:00:27.662 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:27.669 if eqi is None:
2025-07-01 03:00:27.687 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:27.704 atags = btags = ""
2025-07-01 03:00:27.713 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:27.730 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:27.740 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:27.754 if tag == 'replace':
2025-07-01 03:00:27.766 atags += '^' * la
2025-07-01 03:00:27.778 btags += '^' * lb
2025-07-01 03:00:27.790 elif tag == 'delete':
2025-07-01 03:00:27.804 atags += '-' * la
2025-07-01 03:00:27.817 elif tag == 'insert':
2025-07-01 03:00:27.826 btags += '+' * lb
2025-07-01 03:00:27.838 elif tag == 'equal':
2025-07-01 03:00:27.846 atags += ' ' * la
2025-07-01 03:00:27.858 btags += ' ' * lb
2025-07-01 03:00:27.866 else:
2025-07-01 03:00:27.878 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:27.886 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:27.898 else:
2025-07-01 03:00:27.908 # the synch pair is identical
2025-07-01 03:00:27.920 yield '  ' + aelt
2025-07-01 03:00:27.934
2025-07-01 03:00:27.946 # pump out diffs from after the synch point
2025-07-01 03:00:27.962 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:27.970
2025-07-01 03:00:27.980 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:27.994 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:28.002
2025-07-01 03:00:28.014 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:28.022 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:28.036 alo = 95, ahi = 1101
2025-07-01 03:00:28.050 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:28.058 blo = 95, bhi = 1101
2025-07-01 03:00:28.070
2025-07-01 03:00:28.078 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:28.088 g = []
2025-07-01 03:00:28.102 if alo < ahi:
2025-07-01 03:00:28.114 if blo < bhi:
2025-07-01 03:00:28.130 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:28.146 else:
2025-07-01 03:00:28.154 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:28.170 elif blo < bhi:
2025-07-01 03:00:28.182 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:28.194
2025-07-01 03:00:28.202 >       yield from g
2025-07-01 03:00:28.217
2025-07-01 03:00:28.230 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:28.242 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:28.254
2025-07-01 03:00:28.262 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:28.274 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:28.286 alo = 95, ahi = 1101
2025-07-01 03:00:28.297 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:28.310 blo = 95, bhi = 1101
2025-07-01 03:00:28.322
2025-07-01 03:00:28.334 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:28.344 r"""
2025-07-01 03:00:28.358 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:28.369 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:28.382 synch point, and intraline difference marking is done on the
2025-07-01 03:00:28.393 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:28.407
2025-07-01 03:00:28.426 Example:
2025-07-01 03:00:28.433
2025-07-01 03:00:28.442 >>> d = Differ()
2025-07-01 03:00:28.456 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:28.466 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:28.481 >>> print(''.join(results), end="")
2025-07-01 03:00:28.489 - abcDefghiJkl
2025-07-01 03:00:28.510 + abcdefGhijkl
2025-07-01 03:00:28.534 """
2025-07-01 03:00:28.543
2025-07-01 03:00:28.548 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:28.556 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:28.562 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:28.568 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:28.574 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:28.582
2025-07-01 03:00:28.587 # search for the pair that matches best without being identical
2025-07-01 03:00:28.594 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:28.601 # on junk -- unless we have to)
2025-07-01 03:00:28.611 for j in range(blo, bhi):
2025-07-01 03:00:28.617 bj = b[j]
2025-07-01 03:00:28.626 cruncher.set_seq2(bj)
2025-07-01 03:00:28.635 for i in range(alo, ahi):
2025-07-01 03:00:28.643 ai = a[i]
2025-07-01 03:00:28.648 if ai == bj:
2025-07-01 03:00:28.654 if eqi is None:
2025-07-01 03:00:28.661 eqi, eqj = i, j
2025-07-01 03:00:28.677 continue
2025-07-01 03:00:28.694 cruncher.set_seq1(ai)
2025-07-01 03:00:28.709 # computing similarity is expensive, so use the quick
2025-07-01 03:00:28.722 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:28.737 # compares by a factor of 3.
2025-07-01 03:00:28.749 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:28.766 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:28.778 # of the computation is cached by cruncher
2025-07-01 03:00:28.786 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:28.802 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:28.810 cruncher.ratio() > best_ratio:
2025-07-01 03:00:28.820 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:28.834 if best_ratio < cutoff:
2025-07-01 03:00:28.842 # no non-identical "pretty close" pair
2025-07-01 03:00:28.850 if eqi is None:
2025-07-01 03:00:28.865 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:28.874 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:28.886 return
2025-07-01 03:00:28.898 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:28.906 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:28.918 else:
2025-07-01 03:00:28.926 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:28.938 eqi = None
2025-07-01 03:00:28.950
2025-07-01 03:00:28.962 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:28.978 # identical
2025-07-01 03:00:28.986
2025-07-01 03:00:28.998 # pump out diffs from before the synch point
2025-07-01 03:00:29.010 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:29.022
2025-07-01 03:00:29.034 # do intraline marking on the synch pair
2025-07-01 03:00:29.042 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:29.054 if eqi is None:
2025-07-01 03:00:29.066 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:29.078 atags = btags = ""
2025-07-01 03:00:29.090 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:29.102 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:29.114 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:29.126 if tag == 'replace':
2025-07-01 03:00:29.134 atags += '^' * la
2025-07-01 03:00:29.146 btags += '^' * lb
2025-07-01 03:00:29.154 elif tag == 'delete':
2025-07-01 03:00:29.166 atags += '-' * la
2025-07-01 03:00:29.178 elif tag == 'insert':
2025-07-01 03:00:29.187 btags += '+' * lb
2025-07-01 03:00:29.210 elif tag == 'equal':
2025-07-01 03:00:29.224 atags += ' ' * la
2025-07-01 03:00:29.233 btags += ' ' * lb
2025-07-01 03:00:29.247 else:
2025-07-01 03:00:29.262 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:29.271 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:29.285 else:
2025-07-01 03:00:29.296 # the synch pair is identical
2025-07-01 03:00:29.306 yield '  ' + aelt
2025-07-01 03:00:29.321
2025-07-01 03:00:29.334 # pump out diffs from after the synch point
2025-07-01 03:00:29.347 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:29.366
2025-07-01 03:00:29.382 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:29.403 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:29.418
2025-07-01 03:00:29.434 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:29.449 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:29.462 alo = 96, ahi = 1101
2025-07-01 03:00:29.469 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:29.482 blo = 96, bhi = 1101
2025-07-01 03:00:29.490
2025-07-01 03:00:29.504 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:29.514 g = []
2025-07-01 03:00:29.522 if alo < ahi:
2025-07-01 03:00:29.532 if blo < bhi:
2025-07-01 03:00:29.546 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:29.558 else:
2025-07-01 03:00:29.570 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:29.578 elif blo < bhi:
2025-07-01 03:00:29.589 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:29.598
2025-07-01 03:00:29.614 >       yield from g
2025-07-01 03:00:29.622
2025-07-01 03:00:29.630 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:29.642 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:29.649
2025-07-01 03:00:29.666 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:29.682 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:29.690 alo = 96, ahi = 1101
2025-07-01 03:00:29.702 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:29.718 blo = 96, bhi = 1101
2025-07-01 03:00:29.726
2025-07-01 03:00:29.742 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:29.754 r"""
2025-07-01 03:00:29.762 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:29.774 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:29.786 synch point, and intraline difference marking is done on the
2025-07-01 03:00:29.794 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:29.806
2025-07-01 03:00:29.814 Example:
2025-07-01 03:00:29.826
2025-07-01 03:00:29.833 >>> d = Differ()
2025-07-01 03:00:29.840 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:29.848 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:29.854 >>> print(''.join(results), end="")
2025-07-01 03:00:29.861 - abcDefghiJkl
2025-07-01 03:00:29.872 + abcdefGhijkl
2025-07-01 03:00:29.883 """
2025-07-01 03:00:29.887
2025-07-01 03:00:29.892 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:29.897 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:29.902 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:29.906 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:29.911 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:29.915
2025-07-01 03:00:29.920 # search for the pair that matches best without being identical
2025-07-01 03:00:29.925 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:29.929 # on junk -- unless we have to)
2025-07-01 03:00:29.934 for j in range(blo, bhi):
2025-07-01 03:00:29.938 bj = b[j]
2025-07-01 03:00:29.943 cruncher.set_seq2(bj)
2025-07-01 03:00:29.948 for i in range(alo, ahi):
2025-07-01 03:00:29.952 ai = a[i]
2025-07-01 03:00:29.957 if ai == bj:
2025-07-01 03:00:29.962 if eqi is None:
2025-07-01 03:00:29.968 eqi, eqj = i, j
2025-07-01 03:00:29.973 continue
2025-07-01 03:00:29.977 cruncher.set_seq1(ai)
2025-07-01 03:00:29.982 # computing similarity is expensive, so use the quick
2025-07-01 03:00:29.987 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:29.993 # compares by a factor of 3.
2025-07-01 03:00:29.998 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:30.003 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:30.009 # of the computation is cached by cruncher
2025-07-01 03:00:30.015 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:30.020 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:30.025 cruncher.ratio() > best_ratio:
2025-07-01 03:00:30.031 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:30.037 if best_ratio < cutoff:
2025-07-01 03:00:30.042 # no non-identical "pretty close" pair
2025-07-01 03:00:30.048 if eqi is None:
2025-07-01 03:00:30.054 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:30.060 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:30.066 return
2025-07-01 03:00:30.071 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:30.077 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:30.082 else:
2025-07-01 03:00:30.087 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:30.093 eqi = None
2025-07-01 03:00:30.099
2025-07-01 03:00:30.105 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:30.110 # identical
2025-07-01 03:00:30.116
2025-07-01 03:00:30.122 # pump out diffs from before the synch point
2025-07-01 03:00:30.127 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:30.132
2025-07-01 03:00:30.138 # do intraline marking on the synch pair
2025-07-01 03:00:30.143 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:30.149 if eqi is None:
2025-07-01 03:00:30.155 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:30.161 atags = btags = ""
2025-07-01 03:00:30.165 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:30.171 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:30.175 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:30.179 if tag == 'replace':
2025-07-01 03:00:30.184 atags += '^' * la
2025-07-01 03:00:30.188 btags += '^' * lb
2025-07-01 03:00:30.193 elif tag == 'delete':
2025-07-01 03:00:30.197 atags += '-' * la
2025-07-01 03:00:30.201 elif tag == 'insert':
2025-07-01 03:00:30.205 btags += '+' * lb
2025-07-01 03:00:30.210 elif tag == 'equal':
2025-07-01 03:00:30.214 atags += ' ' * la
2025-07-01 03:00:30.219 btags += ' ' * lb
2025-07-01 03:00:30.223 else:
2025-07-01 03:00:30.227 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:30.232 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:30.236 else:
2025-07-01 03:00:30.241 # the synch pair is identical
2025-07-01 03:00:30.245 yield '  ' + aelt
2025-07-01 03:00:30.249
2025-07-01 03:00:30.254 # pump out diffs from after the synch point
2025-07-01 03:00:30.259 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:30.263
2025-07-01 03:00:30.267 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:30.272 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:30.276
2025-07-01 03:00:30.283 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:30.287 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:30.292 alo = 97, ahi = 1101
2025-07-01 03:00:30.297 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:30.301 blo = 97, bhi = 1101
2025-07-01 03:00:30.305
2025-07-01 03:00:30.310 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:30.314 g = []
2025-07-01 03:00:30.318 if alo < ahi:
2025-07-01 03:00:30.323 if blo < bhi:
2025-07-01 03:00:30.327 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:30.331 else:
2025-07-01 03:00:30.336 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:30.340 elif blo < bhi:
2025-07-01 03:00:30.345 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:30.349
2025-07-01 03:00:30.353 >       yield from g
2025-07-01 03:00:30.357
2025-07-01 03:00:30.362 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:30.366 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:30.370
2025-07-01 03:00:30.375 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:30.380 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:30.384 alo = 97, ahi = 1101
2025-07-01 03:00:30.389 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:30.394 blo = 97, bhi = 1101
2025-07-01 03:00:30.398
2025-07-01 03:00:30.402 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:30.406 r"""
2025-07-01 03:00:30.411 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:30.415 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:30.420 synch point, and intraline difference marking is done on the
2025-07-01 03:00:30.424 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:30.429
2025-07-01 03:00:30.433 Example:
2025-07-01 03:00:30.438
2025-07-01 03:00:30.442 >>> d = Differ()
2025-07-01 03:00:30.446 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:30.451 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:30.455 >>> print(''.join(results), end="")
2025-07-01 03:00:30.459 - abcDefghiJkl
2025-07-01 03:00:30.468 + abcdefGhijkl
2025-07-01 03:00:30.477 """
2025-07-01 03:00:30.482
2025-07-01 03:00:30.486 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:30.491 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:30.495 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:30.500 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:30.505 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:30.509
2025-07-01 03:00:30.514 # search for the pair that matches best without being identical
2025-07-01 03:00:30.518 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:30.523 # on junk -- unless we have to)
2025-07-01 03:00:30.527 for j in range(blo, bhi):
2025-07-01 03:00:30.531 bj = b[j]
2025-07-01 03:00:30.536 cruncher.set_seq2(bj)
2025-07-01 03:00:30.540 for i in range(alo, ahi):
2025-07-01 03:00:30.545 ai = a[i]
2025-07-01 03:00:30.549 if ai == bj:
2025-07-01 03:00:30.554 if eqi is None:
2025-07-01 03:00:30.558 eqi, eqj = i, j
2025-07-01 03:00:30.562 continue
2025-07-01 03:00:30.567 cruncher.set_seq1(ai)
2025-07-01 03:00:30.571 # computing similarity is expensive, so use the quick
2025-07-01 03:00:30.576 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:30.580 # compares by a factor of 3.
2025-07-01 03:00:30.585 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:30.589 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:30.594 # of the computation is cached by cruncher
2025-07-01 03:00:30.598 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:30.602 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:30.607 cruncher.ratio() > best_ratio:
2025-07-01 03:00:30.611 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:30.615 if best_ratio < cutoff:
2025-07-01 03:00:30.620 # no non-identical "pretty close" pair
2025-07-01 03:00:30.624 if eqi is None:
2025-07-01 03:00:30.629 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:30.633 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:30.637 return
2025-07-01 03:00:30.642 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:30.647 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:30.651 else:
2025-07-01 03:00:30.656 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:30.660 eqi = None
2025-07-01 03:00:30.665
2025-07-01 03:00:30.670 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:30.678 # identical
2025-07-01 03:00:30.683
2025-07-01 03:00:30.687 # pump out diffs from before the synch point
2025-07-01 03:00:30.692 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:30.696
2025-07-01 03:00:30.701 # do intraline marking on the synch pair
2025-07-01 03:00:30.705 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:30.710 if eqi is None:
2025-07-01 03:00:30.715 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:30.719 atags = btags = ""
2025-07-01 03:00:30.723 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:30.730 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:30.735 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:30.739 if tag == 'replace':
2025-07-01 03:00:30.744 atags += '^' * la
2025-07-01 03:00:30.749 btags += '^' * lb
2025-07-01 03:00:30.753 elif tag == 'delete':
2025-07-01 03:00:30.758 atags += '-' * la
2025-07-01 03:00:30.762 elif tag == 'insert':
2025-07-01 03:00:30.767 btags += '+' * lb
2025-07-01 03:00:30.772 elif tag == 'equal':
2025-07-01 03:00:30.777 atags += ' ' * la
2025-07-01 03:00:30.781 btags += ' ' * lb
2025-07-01 03:00:30.786 else:
2025-07-01 03:00:30.791 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:30.796 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:30.800 else:
2025-07-01 03:00:30.805 # the synch pair is identical
2025-07-01 03:00:30.809 yield '  ' + aelt
2025-07-01 03:00:30.817
2025-07-01 03:00:30.822 # pump out diffs from after the synch point
2025-07-01 03:00:30.827 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:30.834
2025-07-01 03:00:30.843 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:30.848 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:30.853
2025-07-01 03:00:30.858 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:30.868 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:30.878 alo = 98, ahi = 1101
2025-07-01 03:00:30.890 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:30.901 blo = 98, bhi = 1101
2025-07-01 03:00:30.910
2025-07-01 03:00:30.929 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:30.937 g = []
2025-07-01 03:00:30.950 if alo < ahi:
2025-07-01 03:00:30.958 if blo < bhi:
2025-07-01 03:00:30.974 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:30.985 else:
2025-07-01 03:00:30.990 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:31.006 elif blo < bhi:
2025-07-01 03:00:31.022 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:31.030
2025-07-01 03:00:31.042 >       yield from g
2025-07-01 03:00:31.054
2025-07-01 03:00:31.066 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:31.078 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:31.086
2025-07-01 03:00:31.096 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:31.110 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:31.122 alo = 98, ahi = 1101
2025-07-01 03:00:31.138 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:31.150 blo = 98, bhi = 1101
2025-07-01 03:00:31.158
2025-07-01 03:00:31.169 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:31.182 r"""
2025-07-01 03:00:31.190 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:31.200 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:31.214 synch point, and intraline difference marking is done on the
2025-07-01 03:00:31.235 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:31.251
2025-07-01 03:00:31.264 Example:
2025-07-01 03:00:31.274
2025-07-01 03:00:31.282 >>> d = Differ()
2025-07-01 03:00:31.298 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:31.313 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:31.330 >>> print(''.join(results), end="")
2025-07-01 03:00:31.344 - abcDefghiJkl
2025-07-01 03:00:31.373 + abcdefGhijkl
2025-07-01 03:00:31.398 """
2025-07-01 03:00:31.419
2025-07-01 03:00:31.429 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:31.449 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:31.466 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:31.481 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:31.490 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:31.500
2025-07-01 03:00:31.513 # search for the pair that matches best without being identical
2025-07-01 03:00:31.526 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:31.542 # on junk -- unless we have to)
2025-07-01 03:00:31.550 for j in range(blo, bhi):
2025-07-01 03:00:31.566 bj = b[j]
2025-07-01 03:00:31.582 cruncher.set_seq2(bj)
2025-07-01 03:00:31.601 for i in range(alo, ahi):
2025-07-01 03:00:31.612 ai = a[i]
2025-07-01 03:00:31.627 if ai == bj:
2025-07-01 03:00:31.634 if eqi is None:
2025-07-01 03:00:31.651 eqi, eqj = i, j
2025-07-01 03:00:31.662 continue
2025-07-01 03:00:31.671 cruncher.set_seq1(ai)
2025-07-01 03:00:31.687 # computing similarity is expensive, so use the quick
2025-07-01 03:00:31.697 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:31.718 # compares by a factor of 3.
2025-07-01 03:00:31.734 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:31.750 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:31.757 # of the computation is cached by cruncher
2025-07-01 03:00:31.766 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:31.782 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:31.790 cruncher.ratio() > best_ratio:
2025-07-01 03:00:31.802 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:31.813 if best_ratio < cutoff:
2025-07-01 03:00:31.826 # no non-identical "pretty close" pair
2025-07-01 03:00:31.834 if eqi is None:
2025-07-01 03:00:31.845 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:31.862 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:31.870 return
2025-07-01 03:00:31.881 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:31.894 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:31.906 else:
2025-07-01 03:00:31.918 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:31.930 eqi = None
2025-07-01 03:00:31.938
2025-07-01 03:00:31.950 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:31.962 # identical
2025-07-01 03:00:31.970
2025-07-01 03:00:31.986 # pump out diffs from before the synch point
2025-07-01 03:00:32.002 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:32.014
2025-07-01 03:00:32.030 # do intraline marking on the synch pair
2025-07-01 03:00:32.046 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:32.062 if eqi is None:
2025-07-01 03:00:32.078 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:32.094 atags = btags = ""
2025-07-01 03:00:32.110 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:32.126 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:32.146 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:32.154 if tag == 'replace':
2025-07-01 03:00:32.166 atags += '^' * la
2025-07-01 03:00:32.182 btags += '^' * lb
2025-07-01 03:00:32.198 elif tag == 'delete':
2025-07-01 03:00:32.209 atags += '-' * la
2025-07-01 03:00:32.218 elif tag == 'insert':
2025-07-01 03:00:32.234 btags += '+' * lb
2025-07-01 03:00:32.245 elif tag == 'equal':
2025-07-01 03:00:32.254 atags += ' ' * la
2025-07-01 03:00:32.271 btags += ' ' * lb
2025-07-01 03:00:32.287 else:
2025-07-01 03:00:32.302 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:32.318 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:32.334 else:
2025-07-01 03:00:32.351 # the synch pair is identical
2025-07-01 03:00:32.358 yield '  ' + aelt
2025-07-01 03:00:32.374
2025-07-01 03:00:32.386 # pump out diffs from after the synch point
2025-07-01 03:00:32.399 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:32.414
2025-07-01 03:00:32.423 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:32.438 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:32.446
2025-07-01 03:00:32.458 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:32.469 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:32.482 alo = 99, ahi = 1101
2025-07-01 03:00:32.494 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:32.502 blo = 99, bhi = 1101
2025-07-01 03:00:32.514
2025-07-01 03:00:32.526 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:32.534 g = []
2025-07-01 03:00:32.548 if alo < ahi:
2025-07-01 03:00:32.566 if blo < bhi:
2025-07-01 03:00:32.578 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:32.586 else:
2025-07-01 03:00:32.598 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:32.607 elif blo < bhi:
2025-07-01 03:00:32.626 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:32.642
2025-07-01 03:00:32.658 >       yield from g
2025-07-01 03:00:32.675
2025-07-01 03:00:32.682 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:32.689 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:32.697
2025-07-01 03:00:32.704 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:32.713 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:32.719 alo = 99, ahi = 1101
2025-07-01 03:00:32.727 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:32.733 blo = 99, bhi = 1101
2025-07-01 03:00:32.739
2025-07-01 03:00:32.746 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:32.751 r"""
2025-07-01 03:00:32.756 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:32.761 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:32.766 synch point, and intraline difference marking is done on the
2025-07-01 03:00:32.771 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:32.775
2025-07-01 03:00:32.780 Example:
2025-07-01 03:00:32.784
2025-07-01 03:00:32.789 >>> d = Differ()
2025-07-01 03:00:32.793 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:32.798 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:32.803 >>> print(''.join(results), end="")
2025-07-01 03:00:32.808 - abcDefghiJkl
2025-07-01 03:00:32.817 + abcdefGhijkl
2025-07-01 03:00:32.826 """
2025-07-01 03:00:32.831
2025-07-01 03:00:32.835 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:32.841 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:32.845 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:32.850 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:32.855 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:32.861
2025-07-01 03:00:32.866 # search for the pair that matches best without being identical
2025-07-01 03:00:32.871 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:32.876 # on junk -- unless we have to)
2025-07-01 03:00:32.881 for j in range(blo, bhi):
2025-07-01 03:00:32.886 bj = b[j]
2025-07-01 03:00:32.891 cruncher.set_seq2(bj)
2025-07-01 03:00:32.896 for i in range(alo, ahi):
2025-07-01 03:00:32.901 ai = a[i]
2025-07-01 03:00:32.906 if ai == bj:
2025-07-01 03:00:32.911 if eqi is None:
2025-07-01 03:00:32.916 eqi, eqj = i, j
2025-07-01 03:00:32.921 continue
2025-07-01 03:00:32.926 cruncher.set_seq1(ai)
2025-07-01 03:00:32.931 # computing similarity is expensive, so use the quick
2025-07-01 03:00:32.936 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:32.941 # compares by a factor of 3.
2025-07-01 03:00:32.946 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:32.952 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:32.957 # of the computation is cached by cruncher
2025-07-01 03:00:32.962 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:32.967 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:32.971 cruncher.ratio() > best_ratio:
2025-07-01 03:00:32.977 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:32.982 if best_ratio < cutoff:
2025-07-01 03:00:32.987 # no non-identical "pretty close" pair
2025-07-01 03:00:32.992 if eqi is None:
2025-07-01 03:00:32.997 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:33.002 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:33.007 return
2025-07-01 03:00:33.013 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:33.019 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:33.024 else:
2025-07-01 03:00:33.029 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:33.034 eqi = None
2025-07-01 03:00:33.038
2025-07-01 03:00:33.044 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:33.049 # identical
2025-07-01 03:00:33.055
2025-07-01 03:00:33.061 # pump out diffs from before the synch point
2025-07-01 03:00:33.066 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:33.071
2025-07-01 03:00:33.076 # do intraline marking on the synch pair
2025-07-01 03:00:33.081 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:33.086 if eqi is None:
2025-07-01 03:00:33.092 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:33.097 atags = btags = ""
2025-07-01 03:00:33.104 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:33.109 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:33.113 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:33.120 if tag == 'replace':
2025-07-01 03:00:33.125 atags += '^' * la
2025-07-01 03:00:33.129 btags += '^' * lb
2025-07-01 03:00:33.134 elif tag == 'delete':
2025-07-01 03:00:33.139 atags += '-' * la
2025-07-01 03:00:33.147 elif tag == 'insert':
2025-07-01 03:00:33.152 btags += '+' * lb
2025-07-01 03:00:33.157 elif tag == 'equal':
2025-07-01 03:00:33.171 atags += ' ' * la
2025-07-01 03:00:33.181 btags += ' ' * lb
2025-07-01 03:00:33.189 else:
2025-07-01 03:00:33.196 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:33.205 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:33.212 else:
2025-07-01 03:00:33.220 # the synch pair is identical
2025-07-01 03:00:33.227 yield '  ' + aelt
2025-07-01 03:00:33.237
2025-07-01 03:00:33.244 # pump out diffs from after the synch point
2025-07-01 03:00:33.254 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:33.259
2025-07-01 03:00:33.267 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:33.274 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:33.279
2025-07-01 03:00:33.285 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:33.293 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:33.297 alo = 100, ahi = 1101
2025-07-01 03:00:33.305 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:33.309 blo = 100, bhi = 1101
2025-07-01 03:00:33.315
2025-07-01 03:00:33.322 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:33.329 g = []
2025-07-01 03:00:33.341 if alo < ahi:
2025-07-01 03:00:33.350 if blo < bhi:
2025-07-01 03:00:33.356 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:33.363 else:
2025-07-01 03:00:33.368 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:33.388 elif blo < bhi:
2025-07-01 03:00:33.402 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:33.410
2025-07-01 03:00:33.426 >       yield from g
2025-07-01 03:00:33.438
2025-07-01 03:00:33.450 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:33.458 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:33.473
2025-07-01 03:00:33.486 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:33.502 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:33.514 alo = 100, ahi = 1101
2025-07-01 03:00:33.526 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:33.538 blo = 100, bhi = 1101
2025-07-01 03:00:33.550
2025-07-01 03:00:33.558 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:33.577 r"""
2025-07-01 03:00:33.590 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:33.598 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:33.610 synch point, and intraline difference marking is done on the
2025-07-01 03:00:33.622 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:33.630
2025-07-01 03:00:33.646 Example:
2025-07-01 03:00:33.654
2025-07-01 03:00:33.670 >>> d = Differ()
2025-07-01 03:00:33.687 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:33.701 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:33.714 >>> print(''.join(results), end="")
2025-07-01 03:00:33.725 - abcDefghiJkl
2025-07-01 03:00:33.750 + abcdefGhijkl
2025-07-01 03:00:33.770 """
2025-07-01 03:00:33.781
2025-07-01 03:00:33.795 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:33.810 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:33.827 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:33.838 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:33.849 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:33.866
2025-07-01 03:00:33.885 # search for the pair that matches best without being identical
2025-07-01 03:00:33.902 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:33.917 # on junk -- unless we have to)
2025-07-01 03:00:33.930 for j in range(blo, bhi):
2025-07-01 03:00:33.938 bj = b[j]
2025-07-01 03:00:33.950 cruncher.set_seq2(bj)
2025-07-01 03:00:33.958 for i in range(alo, ahi):
2025-07-01 03:00:33.978 ai = a[i]
2025-07-01 03:00:33.986 if ai == bj:
2025-07-01 03:00:33.996 if eqi is None:
2025-07-01 03:00:34.010 eqi, eqj = i, j
2025-07-01 03:00:34.018 continue
2025-07-01 03:00:34.033 cruncher.set_seq1(ai)
2025-07-01 03:00:34.043 # computing similarity is expensive, so use the quick
2025-07-01 03:00:34.058 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:34.070 # compares by a factor of 3.
2025-07-01 03:00:34.086 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:34.098 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:34.110 # of the computation is cached by cruncher
2025-07-01 03:00:34.119 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:34.130 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:34.145 cruncher.ratio() > best_ratio:
2025-07-01 03:00:34.159 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:34.169 if best_ratio < cutoff:
2025-07-01 03:00:34.182 # no non-identical "pretty close" pair
2025-07-01 03:00:34.200 if eqi is None:
2025-07-01 03:00:34.214 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:34.221 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:34.234 return
2025-07-01 03:00:34.246 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:34.262 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:34.276 else:
2025-07-01 03:00:34.294 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:34.302 eqi = None
2025-07-01 03:00:34.318
2025-07-01 03:00:34.330 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:34.342 # identical
2025-07-01 03:00:34.348
2025-07-01 03:00:34.358 # pump out diffs from before the synch point
2025-07-01 03:00:34.370 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:34.382
2025-07-01 03:00:34.398 # do intraline marking on the synch pair
2025-07-01 03:00:34.410 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:34.422 if eqi is None:
2025-07-01 03:00:34.437 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:34.446 atags = btags = ""
2025-07-01 03:00:34.460 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:34.472 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:34.490 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:34.501 if tag == 'replace':
2025-07-01 03:00:34.513 atags += '^' * la
2025-07-01 03:00:34.526 btags += '^' * lb
2025-07-01 03:00:34.539 elif tag == 'delete':
2025-07-01 03:00:34.550 atags += '-' * la
2025-07-01 03:00:34.566 elif tag == 'insert':
2025-07-01 03:00:34.574 btags += '+' * lb
2025-07-01 03:00:34.590 elif tag == 'equal':
2025-07-01 03:00:34.598 atags += ' ' * la
2025-07-01 03:00:34.610 btags += ' ' * lb
2025-07-01 03:00:34.618 else:
2025-07-01 03:00:34.629 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:34.642 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:34.650 else:
2025-07-01 03:00:34.662 # the synch pair is identical
2025-07-01 03:00:34.670 yield '  ' + aelt
2025-07-01 03:00:34.682
2025-07-01 03:00:34.690 # pump out diffs from after the synch point
2025-07-01 03:00:34.702 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:34.713
2025-07-01 03:00:34.733 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:34.742 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:34.756
2025-07-01 03:00:34.769 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:34.785 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:34.801 alo = 101, ahi = 1101
2025-07-01 03:00:34.818 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:34.829 blo = 101, bhi = 1101
2025-07-01 03:00:34.850
2025-07-01 03:00:34.858 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:34.866 g = []
2025-07-01 03:00:34.882 if alo < ahi:
2025-07-01 03:00:34.902 if blo < bhi:
2025-07-01 03:00:34.917 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:34.929 else:
2025-07-01 03:00:34.940 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:34.958 elif blo < bhi:
2025-07-01 03:00:34.966 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:34.980
2025-07-01 03:00:34.998 >       yield from g
2025-07-01 03:00:35.004
2025-07-01 03:00:35.014 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:35.026 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:35.038
2025-07-01 03:00:35.059 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:35.074 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:35.089 alo = 101, ahi = 1101
2025-07-01 03:00:35.105 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:35.118 blo = 101, bhi = 1101
2025-07-01 03:00:35.131
2025-07-01 03:00:35.150 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:35.162 r"""
2025-07-01 03:00:35.174 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:35.190 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:35.206 synch point, and intraline difference marking is done on the
2025-07-01 03:00:35.216 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:35.230
2025-07-01 03:00:35.246 Example:
2025-07-01 03:00:35.254
2025-07-01 03:00:35.266 >>> d = Differ()
2025-07-01 03:00:35.278 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:35.295 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:35.306 >>> print(''.join(results), end="")
2025-07-01 03:00:35.314 - abcDefghiJkl
2025-07-01 03:00:35.334 + abcdefGhijkl
2025-07-01 03:00:35.361 """
2025-07-01 03:00:35.375
2025-07-01 03:00:35.394 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:35.404 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:35.414 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:35.425 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:35.438 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:35.450
2025-07-01 03:00:35.460 # search for the pair that matches best without being identical
2025-07-01 03:00:35.478 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:35.494 # on junk -- unless we have to)
2025-07-01 03:00:35.502 for j in range(blo, bhi):
2025-07-01 03:00:35.517 bj = b[j]
2025-07-01 03:00:35.531 cruncher.set_seq2(bj)
2025-07-01 03:00:35.550 for i in range(alo, ahi):
2025-07-01 03:00:35.558 ai = a[i]
2025-07-01 03:00:35.566 if ai == bj:
2025-07-01 03:00:35.579 if eqi is None:
2025-07-01 03:00:35.598 eqi, eqj = i, j
2025-07-01 03:00:35.614 continue
2025-07-01 03:00:35.622 cruncher.set_seq1(ai)
2025-07-01 03:00:35.630 # computing similarity is expensive, so use the quick
2025-07-01 03:00:35.647 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:35.658 # compares by a factor of 3.
2025-07-01 03:00:35.670 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:35.683 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:35.698 # of the computation is cached by cruncher
2025-07-01 03:00:35.710 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:35.722 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:35.734 cruncher.ratio() > best_ratio:
2025-07-01 03:00:35.750 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:35.762 if best_ratio < cutoff:
2025-07-01 03:00:35.770 # no non-identical "pretty close" pair
2025-07-01 03:00:35.782 if eqi is None:
2025-07-01 03:00:35.800 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:35.815 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:35.830 return
2025-07-01 03:00:35.842 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:35.858 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:35.878 else:
2025-07-01 03:00:35.891 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:35.898 eqi = None
2025-07-01 03:00:35.910
2025-07-01 03:00:35.918 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:35.930 # identical
2025-07-01 03:00:35.942
2025-07-01 03:00:35.953 # pump out diffs from before the synch point
2025-07-01 03:00:35.962 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:35.974
2025-07-01 03:00:35.986 # do intraline marking on the synch pair
2025-07-01 03:00:35.999 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:36.014 if eqi is None:
2025-07-01 03:00:36.029 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:36.043 atags = btags = ""
2025-07-01 03:00:36.050 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:36.066 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:36.078 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:36.090 if tag == 'replace':
2025-07-01 03:00:36.098 atags += '^' * la
2025-07-01 03:00:36.110 btags += '^' * lb
2025-07-01 03:00:36.126 elif tag == 'delete':
2025-07-01 03:00:36.134 atags += '-' * la
2025-07-01 03:00:36.142 elif tag == 'insert':
2025-07-01 03:00:36.150 btags += '+' * lb
2025-07-01 03:00:36.162 elif tag == 'equal':
2025-07-01 03:00:36.173 atags += ' ' * la
2025-07-01 03:00:36.186 btags += ' ' * lb
2025-07-01 03:00:36.196 else:
2025-07-01 03:00:36.206 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:36.218 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:36.226 else:
2025-07-01 03:00:36.242 # the synch pair is identical
2025-07-01 03:00:36.251 yield '  ' + aelt
2025-07-01 03:00:36.258
2025-07-01 03:00:36.270 # pump out diffs from after the synch point
2025-07-01 03:00:36.282 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:36.293
2025-07-01 03:00:36.304 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:36.310 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:36.321
2025-07-01 03:00:36.335 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:36.342 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:36.356 alo = 102, ahi = 1101
2025-07-01 03:00:36.378 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:36.390 blo = 102, bhi = 1101
2025-07-01 03:00:36.404
2025-07-01 03:00:36.412 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:36.426 g = []
2025-07-01 03:00:36.438 if alo < ahi:
2025-07-01 03:00:36.454 if blo < bhi:
2025-07-01 03:00:36.462 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:36.474 else:
2025-07-01 03:00:36.482 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:36.494 elif blo < bhi:
2025-07-01 03:00:36.502 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:36.514
2025-07-01 03:00:36.522 >       yield from g
2025-07-01 03:00:36.537
2025-07-01 03:00:36.546 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:36.558 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:36.570
2025-07-01 03:00:36.582 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:36.598 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:36.610 alo = 102, ahi = 1101
2025-07-01 03:00:36.622 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:36.634 blo = 102, bhi = 1101
2025-07-01 03:00:36.646
2025-07-01 03:00:36.654 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:36.664 r"""
2025-07-01 03:00:36.674 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:36.686 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:36.698 synch point, and intraline difference marking is done on the
2025-07-01 03:00:36.706 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:36.720
2025-07-01 03:00:36.727 Example:
2025-07-01 03:00:36.732
2025-07-01 03:00:36.737 >>> d = Differ()
2025-07-01 03:00:36.742 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:36.747 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:36.752 >>> print(''.join(results), end="")
2025-07-01 03:00:36.757 - abcDefghiJkl
2025-07-01 03:00:36.766 + abcdefGhijkl
2025-07-01 03:00:36.776 """
2025-07-01 03:00:36.781
2025-07-01 03:00:36.786 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:36.790 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:36.795 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:36.799 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:36.804 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:36.809
2025-07-01 03:00:36.814 # search for the pair that matches best without being identical
2025-07-01 03:00:36.819 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:36.824 # on junk -- unless we have to)
2025-07-01 03:00:36.829 for j in range(blo, bhi):
2025-07-01 03:00:36.834 bj = b[j]
2025-07-01 03:00:36.838 cruncher.set_seq2(bj)
2025-07-01 03:00:36.843 for i in range(alo, ahi):
2025-07-01 03:00:36.848 ai = a[i]
2025-07-01 03:00:36.853 if ai == bj:
2025-07-01 03:00:36.858 if eqi is None:
2025-07-01 03:00:36.863 eqi, eqj = i, j
2025-07-01 03:00:36.868 continue
2025-07-01 03:00:36.873 cruncher.set_seq1(ai)
2025-07-01 03:00:36.878 # computing similarity is expensive, so use the quick
2025-07-01 03:00:36.883 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:36.887 # compares by a factor of 3.
2025-07-01 03:00:36.892 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:36.897 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:36.901 # of the computation is cached by cruncher
2025-07-01 03:00:36.906 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:36.911 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:36.915 cruncher.ratio() > best_ratio:
2025-07-01 03:00:36.920 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:36.924 if best_ratio < cutoff:
2025-07-01 03:00:36.929 # no non-identical "pretty close" pair
2025-07-01 03:00:36.934 if eqi is None:
2025-07-01 03:00:36.939 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:36.944 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:36.949 return
2025-07-01 03:00:36.954 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:36.960 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:36.965 else:
2025-07-01 03:00:36.970 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:36.976 eqi = None
2025-07-01 03:00:36.981
2025-07-01 03:00:36.985 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:36.991 # identical
2025-07-01 03:00:36.995
2025-07-01 03:00:37.001 # pump out diffs from before the synch point
2025-07-01 03:00:37.006 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:37.011
2025-07-01 03:00:37.017 # do intraline marking on the synch pair
2025-07-01 03:00:37.022 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:37.027 if eqi is None:
2025-07-01 03:00:37.032 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:37.037 atags = btags = ""
2025-07-01 03:00:37.041 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:37.046 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:37.051 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:37.056 if tag == 'replace':
2025-07-01 03:00:37.061 atags += '^' * la
2025-07-01 03:00:37.066 btags += '^' * lb
2025-07-01 03:00:37.071 elif tag == 'delete':
2025-07-01 03:00:37.075 atags += '-' * la
2025-07-01 03:00:37.080 elif tag == 'insert':
2025-07-01 03:00:37.085 btags += '+' * lb
2025-07-01 03:00:37.091 elif tag == 'equal':
2025-07-01 03:00:37.096 atags += ' ' * la
2025-07-01 03:00:37.100 btags += ' ' * lb
2025-07-01 03:00:37.105 else:
2025-07-01 03:00:37.110 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:37.114 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:37.119 else:
2025-07-01 03:00:37.124 # the synch pair is identical
2025-07-01 03:00:37.128 yield '  ' + aelt
2025-07-01 03:00:37.133
2025-07-01 03:00:37.138 # pump out diffs from after the synch point
2025-07-01 03:00:37.143 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:37.147
2025-07-01 03:00:37.153 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:37.158 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:37.163
2025-07-01 03:00:37.169 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:37.175 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:37.180 alo = 103, ahi = 1101
2025-07-01 03:00:37.185 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:37.190 blo = 103, bhi = 1101
2025-07-01 03:00:37.194
2025-07-01 03:00:37.199 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:37.203 g = []
2025-07-01 03:00:37.208 if alo < ahi:
2025-07-01 03:00:37.212 if blo < bhi:
2025-07-01 03:00:37.217 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:37.222 else:
2025-07-01 03:00:37.227 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:37.231 elif blo < bhi:
2025-07-01 03:00:37.236 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:37.241
2025-07-01 03:00:37.245 >       yield from g
2025-07-01 03:00:37.250
2025-07-01 03:00:37.254 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:37.259 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:37.264
2025-07-01 03:00:37.268 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:37.273 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:37.278 alo = 103, ahi = 1101
2025-07-01 03:00:37.283 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:37.288 blo = 103, bhi = 1101
2025-07-01 03:00:37.292
2025-07-01 03:00:37.297 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:37.302 r"""
2025-07-01 03:00:37.306 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:37.311 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:37.316 synch point, and intraline difference marking is done on the
2025-07-01 03:00:37.320 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:37.325
2025-07-01 03:00:37.329 Example:
2025-07-01 03:00:37.334
2025-07-01 03:00:37.338 >>> d = Differ()
2025-07-01 03:00:37.343 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:37.347 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:37.352 >>> print(''.join(results), end="")
2025-07-01 03:00:37.356 - abcDefghiJkl
2025-07-01 03:00:37.365 + abcdefGhijkl
2025-07-01 03:00:37.374 """
2025-07-01 03:00:37.379
2025-07-01 03:00:37.384 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:37.388 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:37.394 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:37.400 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:37.407 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:37.412
2025-07-01 03:00:37.417 # search for the pair that matches best without being identical
2025-07-01 03:00:37.423 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:37.428 # on junk -- unless we have to)
2025-07-01 03:00:37.434 for j in range(blo, bhi):
2025-07-01 03:00:37.439 bj = b[j]
2025-07-01 03:00:37.443 cruncher.set_seq2(bj)
2025-07-01 03:00:37.449 for i in range(alo, ahi):
2025-07-01 03:00:37.455 ai = a[i]
2025-07-01 03:00:37.461 if ai == bj:
2025-07-01 03:00:37.465 if eqi is None:
2025-07-01 03:00:37.470 eqi, eqj = i, j
2025-07-01 03:00:37.476 continue
2025-07-01 03:00:37.481 cruncher.set_seq1(ai)
2025-07-01 03:00:37.486 # computing similarity is expensive, so use the quick
2025-07-01 03:00:37.491 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:37.497 # compares by a factor of 3.
2025-07-01 03:00:37.502 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:37.507 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:37.512 # of the computation is cached by cruncher
2025-07-01 03:00:37.517 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:37.521 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:37.526 cruncher.ratio() > best_ratio:
2025-07-01 03:00:37.531 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:37.536 if best_ratio < cutoff:
2025-07-01 03:00:37.541 # no non-identical "pretty close" pair
2025-07-01 03:00:37.546 if eqi is None:
2025-07-01 03:00:37.551 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:37.556 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:37.562 return
2025-07-01 03:00:37.567 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:37.572 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:37.577 else:
2025-07-01 03:00:37.582 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:37.587 eqi = None
2025-07-01 03:00:37.593
2025-07-01 03:00:37.598 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:37.602 # identical
2025-07-01 03:00:37.607
2025-07-01 03:00:37.612 # pump out diffs from before the synch point
2025-07-01 03:00:37.617 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:37.621
2025-07-01 03:00:37.626 # do intraline marking on the synch pair
2025-07-01 03:00:37.631 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:37.635 if eqi is None:
2025-07-01 03:00:37.640 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:37.645 atags = btags = ""
2025-07-01 03:00:37.649 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:37.654 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:37.659 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:37.663 if tag == 'replace':
2025-07-01 03:00:37.669 atags += '^' * la
2025-07-01 03:00:37.673 btags += '^' * lb
2025-07-01 03:00:37.678 elif tag == 'delete':
2025-07-01 03:00:37.684 atags += '-' * la
2025-07-01 03:00:37.690 elif tag == 'insert':
2025-07-01 03:00:37.694 btags += '+' * lb
2025-07-01 03:00:37.699 elif tag == 'equal':
2025-07-01 03:00:37.703 atags += ' ' * la
2025-07-01 03:00:37.708 btags += ' ' * lb
2025-07-01 03:00:37.712 else:
2025-07-01 03:00:37.717 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:37.722 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:37.727 else:
2025-07-01 03:00:37.732 # the synch pair is identical
2025-07-01 03:00:37.737 yield '  ' + aelt
2025-07-01 03:00:37.741
2025-07-01 03:00:37.746 # pump out diffs from after the synch point
2025-07-01 03:00:37.751 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:37.756
2025-07-01 03:00:37.760 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:37.765 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:37.769
2025-07-01 03:00:37.774 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:37.779 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:37.783 alo = 104, ahi = 1101
2025-07-01 03:00:37.788 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:37.793 blo = 104, bhi = 1101
2025-07-01 03:00:37.797
2025-07-01 03:00:37.801 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:37.807 g = []
2025-07-01 03:00:37.812 if alo < ahi:
2025-07-01 03:00:37.817 if blo < bhi:
2025-07-01 03:00:37.822 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:37.827 else:
2025-07-01 03:00:37.831 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:37.836 elif blo < bhi:
2025-07-01 03:00:37.840 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:37.845
2025-07-01 03:00:37.849 >       yield from g
2025-07-01 03:00:37.854
2025-07-01 03:00:37.858 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:37.863 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:37.867
2025-07-01 03:00:37.872 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:37.877 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:37.882 alo = 104, ahi = 1101
2025-07-01 03:00:37.887 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:37.891 blo = 104, bhi = 1101
2025-07-01 03:00:37.896
2025-07-01 03:00:37.901 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:37.905 r"""
2025-07-01 03:00:37.910 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:37.914 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:37.919 synch point, and intraline difference marking is done on the
2025-07-01 03:00:37.923 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:37.928
2025-07-01 03:00:37.932 Example:
2025-07-01 03:00:37.937
2025-07-01 03:00:37.942 >>> d = Differ()
2025-07-01 03:00:37.947 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:37.952 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:37.956 >>> print(''.join(results), end="")
2025-07-01 03:00:37.961 - abcDefghiJkl
2025-07-01 03:00:37.970 + abcdefGhijkl
2025-07-01 03:00:37.979 """
2025-07-01 03:00:37.984
2025-07-01 03:00:37.989 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:37.994 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:37.999 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:38.004 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:38.009 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:38.014
2025-07-01 03:00:38.020 # search for the pair that matches best without being identical
2025-07-01 03:00:38.024 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:38.029 # on junk -- unless we have to)
2025-07-01 03:00:38.033 for j in range(blo, bhi):
2025-07-01 03:00:38.038 bj = b[j]
2025-07-01 03:00:38.043 cruncher.set_seq2(bj)
2025-07-01 03:00:38.047 for i in range(alo, ahi):
2025-07-01 03:00:38.052 ai = a[i]
2025-07-01 03:00:38.057 if ai == bj:
2025-07-01 03:00:38.063 if eqi is None:
2025-07-01 03:00:38.068 eqi, eqj = i, j
2025-07-01 03:00:38.073 continue
2025-07-01 03:00:38.077 cruncher.set_seq1(ai)
2025-07-01 03:00:38.082 # computing similarity is expensive, so use the quick
2025-07-01 03:00:38.087 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:38.091 # compares by a factor of 3.
2025-07-01 03:00:38.096 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:38.100 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:38.105 # of the computation is cached by cruncher
2025-07-01 03:00:38.109 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:38.114 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:38.119 cruncher.ratio() > best_ratio:
2025-07-01 03:00:38.124 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:38.128 if best_ratio < cutoff:
2025-07-01 03:00:38.133 # no non-identical "pretty close" pair
2025-07-01 03:00:38.138 if eqi is None:
2025-07-01 03:00:38.143 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:38.147 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:38.152 return
2025-07-01 03:00:38.157 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:38.161 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:38.166 else:
2025-07-01 03:00:38.170 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:38.175 eqi = None
2025-07-01 03:00:38.179
2025-07-01 03:00:38.184 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:38.189 # identical
2025-07-01 03:00:38.195
2025-07-01 03:00:38.200 # pump out diffs from before the synch point
2025-07-01 03:00:38.206 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:38.210
2025-07-01 03:00:38.215 # do intraline marking on the synch pair
2025-07-01 03:00:38.221 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:38.227 if eqi is None:
2025-07-01 03:00:38.233 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:38.238 atags = btags = ""
2025-07-01 03:00:38.244 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:38.251 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:38.257 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:38.263 if tag == 'replace':
2025-07-01 03:00:38.270 atags += '^' * la
2025-07-01 03:00:38.276 btags += '^' * lb
2025-07-01 03:00:38.282 elif tag == 'delete':
2025-07-01 03:00:38.288 atags += '-' * la
2025-07-01 03:00:38.295 elif tag == 'insert':
2025-07-01 03:00:38.301 btags += '+' * lb
2025-07-01 03:00:38.307 elif tag == 'equal':
2025-07-01 03:00:38.313 atags += ' ' * la
2025-07-01 03:00:38.319 btags += ' ' * lb
2025-07-01 03:00:38.326 else:
2025-07-01 03:00:38.332 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:38.338 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:38.344 else:
2025-07-01 03:00:38.350 # the synch pair is identical
2025-07-01 03:00:38.356 yield '  ' + aelt
2025-07-01 03:00:38.362
2025-07-01 03:00:38.368 # pump out diffs from after the synch point
2025-07-01 03:00:38.375 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:38.381
2025-07-01 03:00:38.387 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:38.393 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:38.399
2025-07-01 03:00:38.406 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:38.413 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:38.419 alo = 105, ahi = 1101
2025-07-01 03:00:38.426 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:38.432 blo = 105, bhi = 1101
2025-07-01 03:00:38.438
2025-07-01 03:00:38.445 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:38.452 g = []
2025-07-01 03:00:38.458 if alo < ahi:
2025-07-01 03:00:38.465 if blo < bhi:
2025-07-01 03:00:38.471 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:38.477 else:
2025-07-01 03:00:38.483 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:38.489 elif blo < bhi:
2025-07-01 03:00:38.495 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:38.501
2025-07-01 03:00:38.507 >       yield from g
2025-07-01 03:00:38.513
2025-07-01 03:00:38.520 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:38.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:38.533
2025-07-01 03:00:38.539 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:38.546 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:38.552 alo = 105, ahi = 1101
2025-07-01 03:00:38.559 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:38.566 blo = 105, bhi = 1101
2025-07-01 03:00:38.572
2025-07-01 03:00:38.580 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:38.590 r"""
2025-07-01 03:00:38.595 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:38.600 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:38.605 synch point, and intraline difference marking is done on the
2025-07-01 03:00:38.609 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:38.614
2025-07-01 03:00:38.618 Example:
2025-07-01 03:00:38.622
2025-07-01 03:00:38.626 >>> d = Differ()
2025-07-01 03:00:38.635 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:38.642 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:38.649 >>> print(''.join(results), end="")
2025-07-01 03:00:38.656 - abcDefghiJkl
2025-07-01 03:00:38.669 + abcdefGhijkl
2025-07-01 03:00:38.681 """
2025-07-01 03:00:38.686
2025-07-01 03:00:38.691 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:38.696 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:38.701 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:38.706 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:38.711 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:38.715
2025-07-01 03:00:38.720 # search for the pair that matches best without being identical
2025-07-01 03:00:38.724 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:38.729 # on junk -- unless we have to)
2025-07-01 03:00:38.734 for j in range(blo, bhi):
2025-07-01 03:00:38.738 bj = b[j]
2025-07-01 03:00:38.743 cruncher.set_seq2(bj)
2025-07-01 03:00:38.747 for i in range(alo, ahi):
2025-07-01 03:00:38.752 ai = a[i]
2025-07-01 03:00:38.757 if ai == bj:
2025-07-01 03:00:38.762 if eqi is None:
2025-07-01 03:00:38.767 eqi, eqj = i, j
2025-07-01 03:00:38.772 continue
2025-07-01 03:00:38.778 cruncher.set_seq1(ai)
2025-07-01 03:00:38.784 # computing similarity is expensive, so use the quick
2025-07-01 03:00:38.791 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:38.797 # compares by a factor of 3.
2025-07-01 03:00:38.804 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:38.811 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:38.818 # of the computation is cached by cruncher
2025-07-01 03:00:38.824 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:38.830 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:38.836 cruncher.ratio() > best_ratio:
2025-07-01 03:00:38.842 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:38.849 if best_ratio < cutoff:
2025-07-01 03:00:38.854 # no non-identical "pretty close" pair
2025-07-01 03:00:38.861 if eqi is None:
2025-07-01 03:00:38.867 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:38.873 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:38.879 return
2025-07-01 03:00:38.886 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:38.892 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:38.898 else:
2025-07-01 03:00:38.904 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:38.911 eqi = None
2025-07-01 03:00:38.917
2025-07-01 03:00:38.924 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:38.930 # identical
2025-07-01 03:00:38.936
2025-07-01 03:00:38.942 # pump out diffs from before the synch point
2025-07-01 03:00:38.948 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:38.955
2025-07-01 03:00:38.961 # do intraline marking on the synch pair
2025-07-01 03:00:38.967 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:38.973 if eqi is None:
2025-07-01 03:00:38.979 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:38.985 atags = btags = ""
2025-07-01 03:00:38.991 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:38.997 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:39.004 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:39.010 if tag == 'replace':
2025-07-01 03:00:39.016 atags += '^' * la
2025-07-01 03:00:39.022 btags += '^' * lb
2025-07-01 03:00:39.028 elif tag == 'delete':
2025-07-01 03:00:39.035 atags += '-' * la
2025-07-01 03:00:39.041 elif tag == 'insert':
2025-07-01 03:00:39.047 btags += '+' * lb
2025-07-01 03:00:39.054 elif tag == 'equal':
2025-07-01 03:00:39.060 atags += ' ' * la
2025-07-01 03:00:39.067 btags += ' ' * lb
2025-07-01 03:00:39.073 else:
2025-07-01 03:00:39.079 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:39.085 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:39.091 else:
2025-07-01 03:00:39.098 # the synch pair is identical
2025-07-01 03:00:39.104 yield '  ' + aelt
2025-07-01 03:00:39.110
2025-07-01 03:00:39.116 # pump out diffs from after the synch point
2025-07-01 03:00:39.122 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:39.128
2025-07-01 03:00:39.134 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:39.141 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:39.148
2025-07-01 03:00:39.155 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:39.161 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:39.167 alo = 106, ahi = 1101
2025-07-01 03:00:39.175 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:39.181 blo = 106, bhi = 1101
2025-07-01 03:00:39.187
2025-07-01 03:00:39.193 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:39.199 g = []
2025-07-01 03:00:39.205 if alo < ahi:
2025-07-01 03:00:39.211 if blo < bhi:
2025-07-01 03:00:39.217 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:39.224 else:
2025-07-01 03:00:39.230 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:39.236 elif blo < bhi:
2025-07-01 03:00:39.242 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:39.247
2025-07-01 03:00:39.251 >       yield from g
2025-07-01 03:00:39.256
2025-07-01 03:00:39.260 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:39.265 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:39.269
2025-07-01 03:00:39.274 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:39.279 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:39.283 alo = 106, ahi = 1101
2025-07-01 03:00:39.288 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:39.293 blo = 106, bhi = 1101
2025-07-01 03:00:39.298
2025-07-01 03:00:39.303 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:39.308 r"""
2025-07-01 03:00:39.313 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:39.318 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:39.322 synch point, and intraline difference marking is done on the
2025-07-01 03:00:39.327 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:39.331
2025-07-01 03:00:39.336 Example:
2025-07-01 03:00:39.340
2025-07-01 03:00:39.345 >>> d = Differ()
2025-07-01 03:00:39.350 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:39.355 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:39.360 >>> print(''.join(results), end="")
2025-07-01 03:00:39.365 - abcDefghiJkl
2025-07-01 03:00:39.374 + abcdefGhijkl
2025-07-01 03:00:39.383 """
2025-07-01 03:00:39.387
2025-07-01 03:00:39.391 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:39.396 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:39.401 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:39.405 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:39.410 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:39.414
2025-07-01 03:00:39.418 # search for the pair that matches best without being identical
2025-07-01 03:00:39.423 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:39.429 # on junk -- unless we have to)
2025-07-01 03:00:39.433 for j in range(blo, bhi):
2025-07-01 03:00:39.438 bj = b[j]
2025-07-01 03:00:39.442 cruncher.set_seq2(bj)
2025-07-01 03:00:39.447 for i in range(alo, ahi):
2025-07-01 03:00:39.451 ai = a[i]
2025-07-01 03:00:39.457 if ai == bj:
2025-07-01 03:00:39.461 if eqi is None:
2025-07-01 03:00:39.466 eqi, eqj = i, j
2025-07-01 03:00:39.471 continue
2025-07-01 03:00:39.476 cruncher.set_seq1(ai)
2025-07-01 03:00:39.480 # computing similarity is expensive, so use the quick
2025-07-01 03:00:39.485 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:39.489 # compares by a factor of 3.
2025-07-01 03:00:39.494 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:39.499 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:39.503 # of the computation is cached by cruncher
2025-07-01 03:00:39.508 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:39.512 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:39.517 cruncher.ratio() > best_ratio:
2025-07-01 03:00:39.522 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:39.526 if best_ratio < cutoff:
2025-07-01 03:00:39.531 # no non-identical "pretty close" pair
2025-07-01 03:00:39.535 if eqi is None:
2025-07-01 03:00:39.540 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:39.544 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:39.549 return
2025-07-01 03:00:39.554 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:39.559 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:39.563 else:
2025-07-01 03:00:39.568 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:39.573 eqi = None
2025-07-01 03:00:39.577
2025-07-01 03:00:39.582 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:39.587 # identical
2025-07-01 03:00:39.591
2025-07-01 03:00:39.596 # pump out diffs from before the synch point
2025-07-01 03:00:39.601 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:39.606
2025-07-01 03:00:39.611 # do intraline marking on the synch pair
2025-07-01 03:00:39.616 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:39.622 if eqi is None:
2025-07-01 03:00:39.627 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:39.632 atags = btags = ""
2025-07-01 03:00:39.637 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:39.643 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:39.648 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:39.653 if tag == 'replace':
2025-07-01 03:00:39.659 atags += '^' * la
2025-07-01 03:00:39.664 btags += '^' * lb
2025-07-01 03:00:39.670 elif tag == 'delete':
2025-07-01 03:00:39.675 atags += '-' * la
2025-07-01 03:00:39.681 elif tag == 'insert':
2025-07-01 03:00:39.686 btags += '+' * lb
2025-07-01 03:00:39.691 elif tag == 'equal':
2025-07-01 03:00:39.697 atags += ' ' * la
2025-07-01 03:00:39.702 btags += ' ' * lb
2025-07-01 03:00:39.706 else:
2025-07-01 03:00:39.712 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:39.717 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:39.721 else:
2025-07-01 03:00:39.726 # the synch pair is identical
2025-07-01 03:00:39.732 yield '  ' + aelt
2025-07-01 03:00:39.736
2025-07-01 03:00:39.741 # pump out diffs from after the synch point
2025-07-01 03:00:39.746 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:39.751
2025-07-01 03:00:39.756 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:39.762 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:39.767
2025-07-01 03:00:39.773 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:39.778 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:39.782 alo = 107, ahi = 1101
2025-07-01 03:00:39.788 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:39.792 blo = 107, bhi = 1101
2025-07-01 03:00:39.796
2025-07-01 03:00:39.801 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:39.805 g = []
2025-07-01 03:00:39.809 if alo < ahi:
2025-07-01 03:00:39.814 if blo < bhi:
2025-07-01 03:00:39.818 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:39.822 else:
2025-07-01 03:00:39.827 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:39.831 elif blo < bhi:
2025-07-01 03:00:39.838 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:39.843
2025-07-01 03:00:39.848 >       yield from g
2025-07-01 03:00:39.854
2025-07-01 03:00:39.859 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:39.864 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:39.869
2025-07-01 03:00:39.875 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:39.881 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:39.887 alo = 107, ahi = 1101
2025-07-01 03:00:39.893 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:39.898 blo = 107, bhi = 1101
2025-07-01 03:00:39.903
2025-07-01 03:00:39.909 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:39.914 r"""
2025-07-01 03:00:39.920 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:39.926 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:39.930 synch point, and intraline difference marking is done on the
2025-07-01 03:00:39.935 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:39.940
2025-07-01 03:00:39.945 Example:
2025-07-01 03:00:39.951
2025-07-01 03:00:39.957 >>> d = Differ()
2025-07-01 03:00:39.963 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:39.968 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:39.973 >>> print(''.join(results), end="")
2025-07-01 03:00:39.978 - abcDefghiJkl
2025-07-01 03:00:39.989 + abcdefGhijkl
2025-07-01 03:00:40.000 """
2025-07-01 03:00:40.006
2025-07-01 03:00:40.014 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:40.021 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:40.026 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:40.031 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:40.037 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:40.041
2025-07-01 03:00:40.046 # search for the pair that matches best without being identical
2025-07-01 03:00:40.051 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:40.056 # on junk -- unless we have to)
2025-07-01 03:00:40.060 for j in range(blo, bhi):
2025-07-01 03:00:40.065 bj = b[j]
2025-07-01 03:00:40.069 cruncher.set_seq2(bj)
2025-07-01 03:00:40.074 for i in range(alo, ahi):
2025-07-01 03:00:40.078 ai = a[i]
2025-07-01 03:00:40.083 if ai == bj:
2025-07-01 03:00:40.087 if eqi is None:
2025-07-01 03:00:40.091 eqi, eqj = i, j
2025-07-01 03:00:40.096 continue
2025-07-01 03:00:40.100 cruncher.set_seq1(ai)
2025-07-01 03:00:40.105 # computing similarity is expensive, so use the quick
2025-07-01 03:00:40.109 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:40.114 # compares by a factor of 3.
2025-07-01 03:00:40.118 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:40.123 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:40.127 # of the computation is cached by cruncher
2025-07-01 03:00:40.132 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:40.136 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:40.141 cruncher.ratio() > best_ratio:
2025-07-01 03:00:40.146 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:40.150 if best_ratio < cutoff:
2025-07-01 03:00:40.154 # no non-identical "pretty close" pair
2025-07-01 03:00:40.160 if eqi is None:
2025-07-01 03:00:40.164 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:40.169 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:40.173 return
2025-07-01 03:00:40.178 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:40.182 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:40.187 else:
2025-07-01 03:00:40.192 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:40.196 eqi = None
2025-07-01 03:00:40.201
2025-07-01 03:00:40.206 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:40.210 # identical
2025-07-01 03:00:40.215
2025-07-01 03:00:40.220 # pump out diffs from before the synch point
2025-07-01 03:00:40.225 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:40.229
2025-07-01 03:00:40.234 # do intraline marking on the synch pair
2025-07-01 03:00:40.238 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:40.242 if eqi is None:
2025-07-01 03:00:40.247 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:40.253 atags = btags = ""
2025-07-01 03:00:40.258 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:40.263 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:40.267 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:40.272 if tag == 'replace':
2025-07-01 03:00:40.277 atags += '^' * la
2025-07-01 03:00:40.282 btags += '^' * lb
2025-07-01 03:00:40.287 elif tag == 'delete':
2025-07-01 03:00:40.291 atags += '-' * la
2025-07-01 03:00:40.296 elif tag == 'insert':
2025-07-01 03:00:40.301 btags += '+' * lb
2025-07-01 03:00:40.305 elif tag == 'equal':
2025-07-01 03:00:40.310 atags += ' ' * la
2025-07-01 03:00:40.314 btags += ' ' * lb
2025-07-01 03:00:40.319 else:
2025-07-01 03:00:40.323 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:40.328 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:40.333 else:
2025-07-01 03:00:40.337 # the synch pair is identical
2025-07-01 03:00:40.342 yield '  ' + aelt
2025-07-01 03:00:40.346
2025-07-01 03:00:40.350 # pump out diffs from after the synch point
2025-07-01 03:00:40.355 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:40.360
2025-07-01 03:00:40.365 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:40.370 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:40.375
2025-07-01 03:00:40.379 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:40.384 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:40.389 alo = 108, ahi = 1101
2025-07-01 03:00:40.394 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:40.399 blo = 108, bhi = 1101
2025-07-01 03:00:40.403
2025-07-01 03:00:40.408 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:40.413 g = []
2025-07-01 03:00:40.417 if alo < ahi:
2025-07-01 03:00:40.422 if blo < bhi:
2025-07-01 03:00:40.426 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:40.432 else:
2025-07-01 03:00:40.436 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:40.441 elif blo < bhi:
2025-07-01 03:00:40.446 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:40.452
2025-07-01 03:00:40.459 >       yield from g
2025-07-01 03:00:40.465
2025-07-01 03:00:40.471 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:40.478 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:40.484
2025-07-01 03:00:40.490 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:40.499 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:40.506 alo = 108, ahi = 1101
2025-07-01 03:00:40.513 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:40.519 blo = 108, bhi = 1101
2025-07-01 03:00:40.525
2025-07-01 03:00:40.532 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:40.537 r"""
2025-07-01 03:00:40.542 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:40.547 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:40.551 synch point, and intraline difference marking is done on the
2025-07-01 03:00:40.556 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:40.560
2025-07-01 03:00:40.565 Example:
2025-07-01 03:00:40.569
2025-07-01 03:00:40.574 >>> d = Differ()
2025-07-01 03:00:40.578 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:40.583 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:40.587 >>> print(''.join(results), end="")
2025-07-01 03:00:40.592 - abcDefghiJkl
2025-07-01 03:00:40.601 + abcdefGhijkl
2025-07-01 03:00:40.610 """
2025-07-01 03:00:40.614
2025-07-01 03:00:40.619 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:40.624 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:40.628 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:40.633 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:40.637 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:40.642
2025-07-01 03:00:40.646 # search for the pair that matches best without being identical
2025-07-01 03:00:40.651 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:40.656 # on junk -- unless we have to)
2025-07-01 03:00:40.660 for j in range(blo, bhi):
2025-07-01 03:00:40.665 bj = b[j]
2025-07-01 03:00:40.669 cruncher.set_seq2(bj)
2025-07-01 03:00:40.674 for i in range(alo, ahi):
2025-07-01 03:00:40.680 ai = a[i]
2025-07-01 03:00:40.685 if ai == bj:
2025-07-01 03:00:40.690 if eqi is None:
2025-07-01 03:00:40.695 eqi, eqj = i, j
2025-07-01 03:00:40.699 continue
2025-07-01 03:00:40.704 cruncher.set_seq1(ai)
2025-07-01 03:00:40.710 # computing similarity is expensive, so use the quick
2025-07-01 03:00:40.715 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:40.719 # compares by a factor of 3.
2025-07-01 03:00:40.725 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:40.730 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:40.735 # of the computation is cached by cruncher
2025-07-01 03:00:40.740 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:40.745 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:40.749 cruncher.ratio() > best_ratio:
2025-07-01 03:00:40.754 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:40.758 if best_ratio < cutoff:
2025-07-01 03:00:40.763 # no non-identical "pretty close" pair
2025-07-01 03:00:40.768 if eqi is None:
2025-07-01 03:00:40.772 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:40.778 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:40.783 return
2025-07-01 03:00:40.788 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:40.793 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:40.798 else:
2025-07-01 03:00:40.803 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:40.808 eqi = None
2025-07-01 03:00:40.812
2025-07-01 03:00:40.817 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:40.821 # identical
2025-07-01 03:00:40.827
2025-07-01 03:00:40.832 # pump out diffs from before the synch point
2025-07-01 03:00:40.836 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:40.841
2025-07-01 03:00:40.845 # do intraline marking on the synch pair
2025-07-01 03:00:40.850 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:40.854 if eqi is None:
2025-07-01 03:00:40.859 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:40.863 atags = btags = ""
2025-07-01 03:00:40.868 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:40.873 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:40.878 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:40.883 if tag == 'replace':
2025-07-01 03:00:40.889 atags += '^' * la
2025-07-01 03:00:40.895 btags += '^' * lb
2025-07-01 03:00:40.901 elif tag == 'delete':
2025-07-01 03:00:40.907 atags += '-' * la
2025-07-01 03:00:40.913 elif tag == 'insert':
2025-07-01 03:00:40.918 btags += '+' * lb
2025-07-01 03:00:40.923 elif tag == 'equal':
2025-07-01 03:00:40.929 atags += ' ' * la
2025-07-01 03:00:40.935 btags += ' ' * lb
2025-07-01 03:00:40.940 else:
2025-07-01 03:00:40.946 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:40.952 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:40.958 else:
2025-07-01 03:00:40.965 # the synch pair is identical
2025-07-01 03:00:40.971 yield '  ' + aelt
2025-07-01 03:00:40.977
2025-07-01 03:00:40.984 # pump out diffs from after the synch point
2025-07-01 03:00:40.991 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:40.997
2025-07-01 03:00:41.004 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:41.010 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:41.018
2025-07-01 03:00:41.025 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:41.033 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:41.039 alo = 109, ahi = 1101
2025-07-01 03:00:41.046 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:41.052 blo = 109, bhi = 1101
2025-07-01 03:00:41.058
2025-07-01 03:00:41.065 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:41.071 g = []
2025-07-01 03:00:41.077 if alo < ahi:
2025-07-01 03:00:41.084 if blo < bhi:
2025-07-01 03:00:41.090 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:41.097 else:
2025-07-01 03:00:41.103 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:41.110 elif blo < bhi:
2025-07-01 03:00:41.117 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:41.123
2025-07-01 03:00:41.130 >       yield from g
2025-07-01 03:00:41.135
2025-07-01 03:00:41.140 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:41.145 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:41.150
2025-07-01 03:00:41.155 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:41.161 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:41.166 alo = 109, ahi = 1101
2025-07-01 03:00:41.172 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:41.177 blo = 109, bhi = 1101
2025-07-01 03:00:41.181
2025-07-01 03:00:41.187 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:41.192 r"""
2025-07-01 03:00:41.197 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:41.202 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:41.206 synch point, and intraline difference marking is done on the
2025-07-01 03:00:41.212 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:41.218
2025-07-01 03:00:41.224 Example:
2025-07-01 03:00:41.230
2025-07-01 03:00:41.236 >>> d = Differ()
2025-07-01 03:00:41.241 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:41.246 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:41.251 >>> print(''.join(results), end="")
2025-07-01 03:00:41.256 - abcDefghiJkl
2025-07-01 03:00:41.265 + abcdefGhijkl
2025-07-01 03:00:41.276 """
2025-07-01 03:00:41.281
2025-07-01 03:00:41.285 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:41.290 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:41.295 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:41.301 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:41.306 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:41.311
2025-07-01 03:00:41.315 # search for the pair that matches best without being identical
2025-07-01 03:00:41.320 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:41.325 # on junk -- unless we have to)
2025-07-01 03:00:41.329 for j in range(blo, bhi):
2025-07-01 03:00:41.333 bj = b[j]
2025-07-01 03:00:41.338 cruncher.set_seq2(bj)
2025-07-01 03:00:41.342 for i in range(alo, ahi):
2025-07-01 03:00:41.347 ai = a[i]
2025-07-01 03:00:41.351 if ai == bj:
2025-07-01 03:00:41.355 if eqi is None:
2025-07-01 03:00:41.360 eqi, eqj = i, j
2025-07-01 03:00:41.364 continue
2025-07-01 03:00:41.369 cruncher.set_seq1(ai)
2025-07-01 03:00:41.375 # computing similarity is expensive, so use the quick
2025-07-01 03:00:41.381 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:41.386 # compares by a factor of 3.
2025-07-01 03:00:41.391 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:41.406 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:41.414 # of the computation is cached by cruncher
2025-07-01 03:00:41.426 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:41.434 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:41.446 cruncher.ratio() > best_ratio:
2025-07-01 03:00:41.459 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:41.470 if best_ratio < cutoff:
2025-07-01 03:00:41.486 # no non-identical "pretty close" pair
2025-07-01 03:00:41.494 if eqi is None:
2025-07-01 03:00:41.510 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:41.522 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:41.534 return
2025-07-01 03:00:41.542 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:41.560 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:41.578 else:
2025-07-01 03:00:41.590 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:41.598 eqi = None
2025-07-01 03:00:41.610
2025-07-01 03:00:41.618 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:41.630 # identical
2025-07-01 03:00:41.638
2025-07-01 03:00:41.650 # pump out diffs from before the synch point
2025-07-01 03:00:41.658 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:41.670
2025-07-01 03:00:41.678 # do intraline marking on the synch pair
2025-07-01 03:00:41.690 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:41.706 if eqi is None:
2025-07-01 03:00:41.718 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:41.726 atags = btags = ""
2025-07-01 03:00:41.742 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:41.750 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:41.766 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:41.774 if tag == 'replace':
2025-07-01 03:00:41.794 atags += '^' * la
2025-07-01 03:00:41.802 btags += '^' * lb
2025-07-01 03:00:41.810 elif tag == 'delete':
2025-07-01 03:00:41.818 atags += '-' * la
2025-07-01 03:00:41.830 elif tag == 'insert':
2025-07-01 03:00:41.842 btags += '+' * lb
2025-07-01 03:00:41.850 elif tag == 'equal':
2025-07-01 03:00:41.862 atags += ' ' * la
2025-07-01 03:00:41.874 btags += ' ' * lb
2025-07-01 03:00:41.882 else:
2025-07-01 03:00:41.894 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:41.902 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:41.914 else:
2025-07-01 03:00:41.922 # the synch pair is identical
2025-07-01 03:00:41.933 yield '  ' + aelt
2025-07-01 03:00:41.942
2025-07-01 03:00:41.950 # pump out diffs from after the synch point
2025-07-01 03:00:41.966 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:41.974
2025-07-01 03:00:41.990 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:41.998 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:42.014
2025-07-01 03:00:42.023 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:42.038 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:42.050 alo = 110, ahi = 1101
2025-07-01 03:00:42.061 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:42.079 blo = 110, bhi = 1101
2025-07-01 03:00:42.090
2025-07-01 03:00:42.102 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:42.110 g = []
2025-07-01 03:00:42.118 if alo < ahi:
2025-07-01 03:00:42.134 if blo < bhi:
2025-07-01 03:00:42.146 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:42.156 else:
2025-07-01 03:00:42.166 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:42.177 elif blo < bhi:
2025-07-01 03:00:42.194 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:42.202
2025-07-01 03:00:42.214 >       yield from g
2025-07-01 03:00:42.226
2025-07-01 03:00:42.234 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:42.246 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:42.254
2025-07-01 03:00:42.266 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:42.278 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:42.294 alo = 110, ahi = 1101
2025-07-01 03:00:42.306 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:42.314 blo = 110, bhi = 1101
2025-07-01 03:00:42.326
2025-07-01 03:00:42.334 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:42.346 r"""
2025-07-01 03:00:42.354 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:42.366 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:42.374 synch point, and intraline difference marking is done on the
2025-07-01 03:00:42.386 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:42.402
2025-07-01 03:00:42.414 Example:
2025-07-01 03:00:42.424
2025-07-01 03:00:42.438 >>> d = Differ()
2025-07-01 03:00:42.450 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:42.466 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:42.476 >>> print(''.join(results), end="")
2025-07-01 03:00:42.491 - abcDefghiJkl
2025-07-01 03:00:42.514 + abcdefGhijkl
2025-07-01 03:00:42.534 """
2025-07-01 03:00:42.546
2025-07-01 03:00:42.556 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:42.570 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:42.581 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:42.594 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:42.612 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:42.621
2025-07-01 03:00:42.634 # search for the pair that matches best without being identical
2025-07-01 03:00:42.654 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:42.664 # on junk -- unless we have to)
2025-07-01 03:00:42.682 for j in range(blo, bhi):
2025-07-01 03:00:42.690 bj = b[j]
2025-07-01 03:00:42.707 cruncher.set_seq2(bj)
2025-07-01 03:00:42.718 for i in range(alo, ahi):
2025-07-01 03:00:42.730 ai = a[i]
2025-07-01 03:00:42.741 if ai == bj:
2025-07-01 03:00:42.755 if eqi is None:
2025-07-01 03:00:42.772 eqi, eqj = i, j
2025-07-01 03:00:42.791 continue
2025-07-01 03:00:42.803 cruncher.set_seq1(ai)
2025-07-01 03:00:42.818 # computing similarity is expensive, so use the quick
2025-07-01 03:00:42.835 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:42.847 # compares by a factor of 3.
2025-07-01 03:00:42.858 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:42.877 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:42.890 # of the computation is cached by cruncher
2025-07-01 03:00:42.906 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:42.918 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:42.937 cruncher.ratio() > best_ratio:
2025-07-01 03:00:42.951 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:42.969 if best_ratio < cutoff:
2025-07-01 03:00:42.978 # no non-identical "pretty close" pair
2025-07-01 03:00:42.994 if eqi is None:
2025-07-01 03:00:43.009 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:43.023 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:43.035 return
2025-07-01 03:00:43.050 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:43.062 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:43.077 else:
2025-07-01 03:00:43.094 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:43.110 eqi = None
2025-07-01 03:00:43.126
2025-07-01 03:00:43.141 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:43.154 # identical
2025-07-01 03:00:43.169
2025-07-01 03:00:43.185 # pump out diffs from before the synch point
2025-07-01 03:00:43.194 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:43.213
2025-07-01 03:00:43.226 # do intraline marking on the synch pair
2025-07-01 03:00:43.241 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:43.254 if eqi is None:
2025-07-01 03:00:43.269 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:43.280 atags = btags = ""
2025-07-01 03:00:43.289 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:43.302 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:43.314 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:43.332 if tag == 'replace':
2025-07-01 03:00:43.346 atags += '^' * la
2025-07-01 03:00:43.358 btags += '^' * lb
2025-07-01 03:00:43.367 elif tag == 'delete':
2025-07-01 03:00:43.386 atags += '-' * la
2025-07-01 03:00:43.394 elif tag == 'insert':
2025-07-01 03:00:43.404 btags += '+' * lb
2025-07-01 03:00:43.418 elif tag == 'equal':
2025-07-01 03:00:43.426 atags += ' ' * la
2025-07-01 03:00:43.438 btags += ' ' * lb
2025-07-01 03:00:43.450 else:
2025-07-01 03:00:43.458 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:43.469 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:43.487 else:
2025-07-01 03:00:43.502 # the synch pair is identical
2025-07-01 03:00:43.518 yield '  ' + aelt
2025-07-01 03:00:43.534
2025-07-01 03:00:43.550 # pump out diffs from after the synch point
2025-07-01 03:00:43.558 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:43.570
2025-07-01 03:00:43.581 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:43.594 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:43.606
2025-07-01 03:00:43.614 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:43.630 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:43.642 alo = 111, ahi = 1101
2025-07-01 03:00:43.654 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:43.663 blo = 111, bhi = 1101
2025-07-01 03:00:43.678
2025-07-01 03:00:43.686 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:43.698 g = []
2025-07-01 03:00:43.706 if alo < ahi:
2025-07-01 03:00:43.718 if blo < bhi:
2025-07-01 03:00:43.726 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:43.738 else:
2025-07-01 03:00:43.748 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:43.762 elif blo < bhi:
2025-07-01 03:00:43.774 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:43.783
2025-07-01 03:00:43.798 >       yield from g
2025-07-01 03:00:43.806
2025-07-01 03:00:43.817 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:43.832 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:43.842
2025-07-01 03:00:43.853 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:43.870 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:43.881 alo = 111, ahi = 1101
2025-07-01 03:00:43.894 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:43.902 blo = 111, bhi = 1101
2025-07-01 03:00:43.910
2025-07-01 03:00:43.926 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:43.938 r"""
2025-07-01 03:00:43.948 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:43.962 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:43.969 synch point, and intraline difference marking is done on the
2025-07-01 03:00:43.986 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:43.992
2025-07-01 03:00:44.007 Example:
2025-07-01 03:00:44.018
2025-07-01 03:00:44.033 >>> d = Differ()
2025-07-01 03:00:44.042 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:44.051 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:44.069 >>> print(''.join(results), end="")
2025-07-01 03:00:44.084 - abcDefghiJkl
2025-07-01 03:00:44.102 + abcdefGhijkl
2025-07-01 03:00:44.124 """
2025-07-01 03:00:44.135
2025-07-01 03:00:44.145 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:44.155 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:44.177 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:44.192 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:44.202 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:44.217
2025-07-01 03:00:44.230 # search for the pair that matches best without being identical
2025-07-01 03:00:44.246 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:44.258 # on junk -- unless we have to)
2025-07-01 03:00:44.270 for j in range(blo, bhi):
2025-07-01 03:00:44.279 bj = b[j]
2025-07-01 03:00:44.290 cruncher.set_seq2(bj)
2025-07-01 03:00:44.302 for i in range(alo, ahi):
2025-07-01 03:00:44.310 ai = a[i]
2025-07-01 03:00:44.322 if ai == bj:
2025-07-01 03:00:44.334 if eqi is None:
2025-07-01 03:00:44.346 eqi, eqj = i, j
2025-07-01 03:00:44.354 continue
2025-07-01 03:00:44.366 cruncher.set_seq1(ai)
2025-07-01 03:00:44.378 # computing similarity is expensive, so use the quick
2025-07-01 03:00:44.386 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:44.398 # compares by a factor of 3.
2025-07-01 03:00:44.410 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:44.419 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:44.432 # of the computation is cached by cruncher
2025-07-01 03:00:44.443 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:44.459 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:44.476 cruncher.ratio() > best_ratio:
2025-07-01 03:00:44.486 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:44.503 if best_ratio < cutoff:
2025-07-01 03:00:44.514 # no non-identical "pretty close" pair
2025-07-01 03:00:44.526 if eqi is None:
2025-07-01 03:00:44.538 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:44.546 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:44.555 return
2025-07-01 03:00:44.570 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:44.581 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:44.592 else:
2025-07-01 03:00:44.606 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:44.622 eqi = None
2025-07-01 03:00:44.633
2025-07-01 03:00:44.646 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:44.656 # identical
2025-07-01 03:00:44.670
2025-07-01 03:00:44.682 # pump out diffs from before the synch point
2025-07-01 03:00:44.694 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:44.706
2025-07-01 03:00:44.718 # do intraline marking on the synch pair
2025-07-01 03:00:44.726 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:44.738 if eqi is None:
2025-07-01 03:00:44.750 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:44.762 atags = btags = ""
2025-07-01 03:00:44.774 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:44.790 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:44.798 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:44.810 if tag == 'replace':
2025-07-01 03:00:44.822 atags += '^' * la
2025-07-01 03:00:44.834 btags += '^' * lb
2025-07-01 03:00:44.842 elif tag == 'delete':
2025-07-01 03:00:44.854 atags += '-' * la
2025-07-01 03:00:44.862 elif tag == 'insert':
2025-07-01 03:00:44.874 btags += '+' * lb
2025-07-01 03:00:44.882 elif tag == 'equal':
2025-07-01 03:00:44.894 atags += ' ' * la
2025-07-01 03:00:44.902 btags += ' ' * lb
2025-07-01 03:00:44.914 else:
2025-07-01 03:00:44.922 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:44.934 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:44.939 else:
2025-07-01 03:00:44.956 # the synch pair is identical
2025-07-01 03:00:44.966 yield '  ' + aelt
2025-07-01 03:00:44.978
2025-07-01 03:00:44.986 # pump out diffs from after the synch point
2025-07-01 03:00:44.996 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:45.008
2025-07-01 03:00:45.022 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:45.038 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:45.050
2025-07-01 03:00:45.066 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:45.081 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:45.094 alo = 114, ahi = 1101
2025-07-01 03:00:45.110 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:45.120 blo = 114, bhi = 1101
2025-07-01 03:00:45.134
2025-07-01 03:00:45.150 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:45.162 g = []
2025-07-01 03:00:45.170 if alo < ahi:
2025-07-01 03:00:45.184 if blo < bhi:
2025-07-01 03:00:45.194 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:45.206 else:
2025-07-01 03:00:45.218 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:45.226 elif blo < bhi:
2025-07-01 03:00:45.238 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:45.246
2025-07-01 03:00:45.260 >       yield from g
2025-07-01 03:00:45.273
2025-07-01 03:00:45.286 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:45.295 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:45.312
2025-07-01 03:00:45.322 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:45.331 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:45.345 alo = 114, ahi = 1101
2025-07-01 03:00:45.355 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:45.366 blo = 114, bhi = 1101
2025-07-01 03:00:45.375
2025-07-01 03:00:45.388 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:45.403 r"""
2025-07-01 03:00:45.415 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:45.426 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:45.437 synch point, and intraline difference marking is done on the
2025-07-01 03:00:45.458 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:45.468
2025-07-01 03:00:45.478 Example:
2025-07-01 03:00:45.494
2025-07-01 03:00:45.506 >>> d = Differ()
2025-07-01 03:00:45.518 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:45.530 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:45.542 >>> print(''.join(results), end="")
2025-07-01 03:00:45.550 - abcDefghiJkl
2025-07-01 03:00:45.574 + abcdefGhijkl
2025-07-01 03:00:45.594 """
2025-07-01 03:00:45.610
2025-07-01 03:00:45.622 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:45.634 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:45.646 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:45.658 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:45.669 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:45.686
2025-07-01 03:00:45.694 # search for the pair that matches best without being identical
2025-07-01 03:00:45.706 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:45.714 # on junk -- unless we have to)
2025-07-01 03:00:45.725 for j in range(blo, bhi):
2025-07-01 03:00:45.735 bj = b[j]
2025-07-01 03:00:45.746 cruncher.set_seq2(bj)
2025-07-01 03:00:45.754 for i in range(alo, ahi):
2025-07-01 03:00:45.766 ai = a[i]
2025-07-01 03:00:45.778 if ai == bj:
2025-07-01 03:00:45.783 if eqi is None:
2025-07-01 03:00:45.787 eqi, eqj = i, j
2025-07-01 03:00:45.792 continue
2025-07-01 03:00:45.797 cruncher.set_seq1(ai)
2025-07-01 03:00:45.802 # computing similarity is expensive, so use the quick
2025-07-01 03:00:45.807 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:45.811 # compares by a factor of 3.
2025-07-01 03:00:45.816 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:45.821 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:45.826 # of the computation is cached by cruncher
2025-07-01 03:00:45.830 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:45.835 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:45.840 cruncher.ratio() > best_ratio:
2025-07-01 03:00:45.844 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:45.849 if best_ratio < cutoff:
2025-07-01 03:00:45.853 # no non-identical "pretty close" pair
2025-07-01 03:00:45.857 if eqi is None:
2025-07-01 03:00:45.862 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:45.867 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:45.871 return
2025-07-01 03:00:45.876 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:45.880 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:45.885 else:
2025-07-01 03:00:45.889 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:45.894 eqi = None
2025-07-01 03:00:45.898
2025-07-01 03:00:45.903 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:45.908 # identical
2025-07-01 03:00:45.912
2025-07-01 03:00:45.917 # pump out diffs from before the synch point
2025-07-01 03:00:45.921 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:45.926
2025-07-01 03:00:45.930 # do intraline marking on the synch pair
2025-07-01 03:00:45.935 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:45.939 if eqi is None:
2025-07-01 03:00:45.944 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:45.948 atags = btags = ""
2025-07-01 03:00:45.953 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:45.957 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:45.962 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:45.966 if tag == 'replace':
2025-07-01 03:00:45.971 atags += '^' * la
2025-07-01 03:00:45.975 btags += '^' * lb
2025-07-01 03:00:45.979 elif tag == 'delete':
2025-07-01 03:00:45.984 atags += '-' * la
2025-07-01 03:00:45.988 elif tag == 'insert':
2025-07-01 03:00:45.993 btags += '+' * lb
2025-07-01 03:00:45.997 elif tag == 'equal':
2025-07-01 03:00:46.002 atags += ' ' * la
2025-07-01 03:00:46.007 btags += ' ' * lb
2025-07-01 03:00:46.012 else:
2025-07-01 03:00:46.017 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:46.022 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:46.026 else:
2025-07-01 03:00:46.031 # the synch pair is identical
2025-07-01 03:00:46.036 yield '  ' + aelt
2025-07-01 03:00:46.040
2025-07-01 03:00:46.045 # pump out diffs from after the synch point
2025-07-01 03:00:46.050 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:46.054
2025-07-01 03:00:46.059 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:46.064 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:46.068
2025-07-01 03:00:46.072 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:46.078 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:46.082 alo = 115, ahi = 1101
2025-07-01 03:00:46.087 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:46.092 blo = 115, bhi = 1101
2025-07-01 03:00:46.096
2025-07-01 03:00:46.101 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:46.105 g = []
2025-07-01 03:00:46.110 if alo < ahi:
2025-07-01 03:00:46.114 if blo < bhi:
2025-07-01 03:00:46.119 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:46.123 else:
2025-07-01 03:00:46.128 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:46.132 elif blo < bhi:
2025-07-01 03:00:46.136 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:46.141
2025-07-01 03:00:46.145 >       yield from g
2025-07-01 03:00:46.150
2025-07-01 03:00:46.156 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:46.173 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:46.182
2025-07-01 03:00:46.194 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:46.206 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:46.218 alo = 115, ahi = 1101
2025-07-01 03:00:46.234 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:46.241 blo = 115, bhi = 1101
2025-07-01 03:00:46.250
2025-07-01 03:00:46.262 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:46.282 r"""
2025-07-01 03:00:46.290 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:46.302 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:46.318 synch point, and intraline difference marking is done on the
2025-07-01 03:00:46.326 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:46.334
2025-07-01 03:00:46.353 Example:
2025-07-01 03:00:46.359
2025-07-01 03:00:46.373 >>> d = Differ()
2025-07-01 03:00:46.391 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:46.402 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:46.414 >>> print(''.join(results), end="")
2025-07-01 03:00:46.426 - abcDefghiJkl
2025-07-01 03:00:46.457 + abcdefGhijkl
2025-07-01 03:00:46.485 """
2025-07-01 03:00:46.495
2025-07-01 03:00:46.513 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:46.530 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:46.537 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:46.553 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:46.569 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:46.582
2025-07-01 03:00:46.598 # search for the pair that matches best without being identical
2025-07-01 03:00:46.614 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:46.634 # on junk -- unless we have to)
2025-07-01 03:00:46.646 for j in range(blo, bhi):
2025-07-01 03:00:46.658 bj = b[j]
2025-07-01 03:00:46.666 cruncher.set_seq2(bj)
2025-07-01 03:00:46.678 for i in range(alo, ahi):
2025-07-01 03:00:46.686 ai = a[i]
2025-07-01 03:00:46.698 if ai == bj:
2025-07-01 03:00:46.706 if eqi is None:
2025-07-01 03:00:46.714 eqi, eqj = i, j
2025-07-01 03:00:46.726 continue
2025-07-01 03:00:46.734 cruncher.set_seq1(ai)
2025-07-01 03:00:46.745 # computing similarity is expensive, so use the quick
2025-07-01 03:00:46.758 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:46.769 # compares by a factor of 3.
2025-07-01 03:00:46.782 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:46.790 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:46.801 # of the computation is cached by cruncher
2025-07-01 03:00:46.811 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:46.818 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:46.829 cruncher.ratio() > best_ratio:
2025-07-01 03:00:46.844 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:46.856 if best_ratio < cutoff:
2025-07-01 03:00:46.877 # no non-identical "pretty close" pair
2025-07-01 03:00:46.889 if eqi is None:
2025-07-01 03:00:46.905 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:46.914 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:46.927 return
2025-07-01 03:00:46.942 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:46.958 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:46.974 else:
2025-07-01 03:00:46.983 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:46.998 eqi = None
2025-07-01 03:00:47.010
2025-07-01 03:00:47.022 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:47.030 # identical
2025-07-01 03:00:47.038
2025-07-01 03:00:47.050 # pump out diffs from before the synch point
2025-07-01 03:00:47.058 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:47.066
2025-07-01 03:00:47.082 # do intraline marking on the synch pair
2025-07-01 03:00:47.093 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:47.102 if eqi is None:
2025-07-01 03:00:47.119 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:47.134 atags = btags = ""
2025-07-01 03:00:47.143 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:47.154 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:47.174 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:47.189 if tag == 'replace':
2025-07-01 03:00:47.203 atags += '^' * la
2025-07-01 03:00:47.219 btags += '^' * lb
2025-07-01 03:00:47.234 elif tag == 'delete':
2025-07-01 03:00:47.249 atags += '-' * la
2025-07-01 03:00:47.265 elif tag == 'insert':
2025-07-01 03:00:47.285 btags += '+' * lb
2025-07-01 03:00:47.292 elif tag == 'equal':
2025-07-01 03:00:47.310 atags += ' ' * la
2025-07-01 03:00:47.322 btags += ' ' * lb
2025-07-01 03:00:47.330 else:
2025-07-01 03:00:47.342 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:47.350 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:47.362 else:
2025-07-01 03:00:47.370 # the synch pair is identical
2025-07-01 03:00:47.382 yield '  ' + aelt
2025-07-01 03:00:47.399
2025-07-01 03:00:47.407 # pump out diffs from after the synch point
2025-07-01 03:00:47.427 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:47.434
2025-07-01 03:00:47.442 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:47.454 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:47.466
2025-07-01 03:00:47.474 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:47.486 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:47.498 alo = 116, ahi = 1101
2025-07-01 03:00:47.514 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:47.522 blo = 116, bhi = 1101
2025-07-01 03:00:47.530
2025-07-01 03:00:47.542 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:47.554 g = []
2025-07-01 03:00:47.564 if alo < ahi:
2025-07-01 03:00:47.582 if blo < bhi:
2025-07-01 03:00:47.590 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:47.602 else:
2025-07-01 03:00:47.614 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:47.621 elif blo < bhi:
2025-07-01 03:00:47.634 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:47.646
2025-07-01 03:00:47.654 >       yield from g
2025-07-01 03:00:47.670
2025-07-01 03:00:47.678 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:47.688 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:47.698
2025-07-01 03:00:47.714 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:47.730 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:47.742 alo = 116, ahi = 1101
2025-07-01 03:00:47.754 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:47.766 blo = 116, bhi = 1101
2025-07-01 03:00:47.774
2025-07-01 03:00:47.786 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:47.794 r"""
2025-07-01 03:00:47.806 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:47.814 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:47.826 synch point, and intraline difference marking is done on the
2025-07-01 03:00:47.833 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:47.846
2025-07-01 03:00:47.854 Example:
2025-07-01 03:00:47.866
2025-07-01 03:00:47.873 >>> d = Differ()
2025-07-01 03:00:47.881 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:47.894 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:47.902 >>> print(''.join(results), end="")
2025-07-01 03:00:47.918 - abcDefghiJkl
2025-07-01 03:00:47.938 + abcdefGhijkl
2025-07-01 03:00:47.958 """
2025-07-01 03:00:47.970
2025-07-01 03:00:47.978 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:47.989 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:47.998 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:48.008 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:48.024 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:48.034
2025-07-01 03:00:48.044 # search for the pair that matches best without being identical
2025-07-01 03:00:48.054 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:48.066 # on junk -- unless we have to)
2025-07-01 03:00:48.073 for j in range(blo, bhi):
2025-07-01 03:00:48.090 bj = b[j]
2025-07-01 03:00:48.098 cruncher.set_seq2(bj)
2025-07-01 03:00:48.110 for i in range(alo, ahi):
2025-07-01 03:00:48.117 ai = a[i]
2025-07-01 03:00:48.134 if ai == bj:
2025-07-01 03:00:48.141 if eqi is None:
2025-07-01 03:00:48.153 eqi, eqj = i, j
2025-07-01 03:00:48.160 continue
2025-07-01 03:00:48.174 cruncher.set_seq1(ai)
2025-07-01 03:00:48.186 # computing similarity is expensive, so use the quick
2025-07-01 03:00:48.194 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:48.204 # compares by a factor of 3.
2025-07-01 03:00:48.218 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:48.230 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:48.242 # of the computation is cached by cruncher
2025-07-01 03:00:48.250 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:48.265 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:48.281 cruncher.ratio() > best_ratio:
2025-07-01 03:00:48.289 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:48.301 if best_ratio < cutoff:
2025-07-01 03:00:48.310 # no non-identical "pretty close" pair
2025-07-01 03:00:48.318 if eqi is None:
2025-07-01 03:00:48.330 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:48.348 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:48.359 return
2025-07-01 03:00:48.373 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:48.382 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:48.393 else:
2025-07-01 03:00:48.401 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:48.414 eqi = None
2025-07-01 03:00:48.433
2025-07-01 03:00:48.446 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:48.455 # identical
2025-07-01 03:00:48.460
2025-07-01 03:00:48.464 # pump out diffs from before the synch point
2025-07-01 03:00:48.469 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:48.473
2025-07-01 03:00:48.478 # do intraline marking on the synch pair
2025-07-01 03:00:48.483 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:48.487 if eqi is None:
2025-07-01 03:00:48.492 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:48.496 atags = btags = ""
2025-07-01 03:00:48.500 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:48.505 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:48.509 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:48.514 if tag == 'replace':
2025-07-01 03:00:48.520 atags += '^' * la
2025-07-01 03:00:48.525 btags += '^' * lb
2025-07-01 03:00:48.529 elif tag == 'delete':
2025-07-01 03:00:48.534 atags += '-' * la
2025-07-01 03:00:48.538 elif tag == 'insert':
2025-07-01 03:00:48.543 btags += '+' * lb
2025-07-01 03:00:48.547 elif tag == 'equal':
2025-07-01 03:00:48.552 atags += ' ' * la
2025-07-01 03:00:48.556 btags += ' ' * lb
2025-07-01 03:00:48.561 else:
2025-07-01 03:00:48.566 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:48.572 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:48.578 else:
2025-07-01 03:00:48.585 # the synch pair is identical
2025-07-01 03:00:48.591 yield '  ' + aelt
2025-07-01 03:00:48.596
2025-07-01 03:00:48.601 # pump out diffs from after the synch point
2025-07-01 03:00:48.607 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:48.611
2025-07-01 03:00:48.616 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:48.621 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:48.626
2025-07-01 03:00:48.630 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:48.637 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:48.641 alo = 117, ahi = 1101
2025-07-01 03:00:48.647 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:48.652 blo = 117, bhi = 1101
2025-07-01 03:00:48.657
2025-07-01 03:00:48.662 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:48.667 g = []
2025-07-01 03:00:48.672 if alo < ahi:
2025-07-01 03:00:48.677 if blo < bhi:
2025-07-01 03:00:48.682 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:48.687 else:
2025-07-01 03:00:48.693 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:48.698 elif blo < bhi:
2025-07-01 03:00:48.703 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:48.708
2025-07-01 03:00:48.713 >       yield from g
2025-07-01 03:00:48.717
2025-07-01 03:00:48.722 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:48.727 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:48.732
2025-07-01 03:00:48.737 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:48.742 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:48.746 alo = 117, ahi = 1101
2025-07-01 03:00:48.752 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:48.757 blo = 117, bhi = 1101
2025-07-01 03:00:48.761
2025-07-01 03:00:48.766 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:48.770 r"""
2025-07-01 03:00:48.775 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:48.780 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:48.785 synch point, and intraline difference marking is done on the
2025-07-01 03:00:48.789 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:48.794
2025-07-01 03:00:48.799 Example:
2025-07-01 03:00:48.803
2025-07-01 03:00:48.808 >>> d = Differ()
2025-07-01 03:00:48.813 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:48.818 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:48.822 >>> print(''.join(results), end="")
2025-07-01 03:00:48.827 - abcDefghiJkl
2025-07-01 03:00:48.836 + abcdefGhijkl
2025-07-01 03:00:48.846 """
2025-07-01 03:00:48.850
2025-07-01 03:00:48.855 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:48.860 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:48.864 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:48.869 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:48.873 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:48.877
2025-07-01 03:00:48.882 # search for the pair that matches best without being identical
2025-07-01 03:00:48.887 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:48.891 # on junk -- unless we have to)
2025-07-01 03:00:48.895 for j in range(blo, bhi):
2025-07-01 03:00:48.900 bj = b[j]
2025-07-01 03:00:48.904 cruncher.set_seq2(bj)
2025-07-01 03:00:48.909 for i in range(alo, ahi):
2025-07-01 03:00:48.914 ai = a[i]
2025-07-01 03:00:48.918 if ai == bj:
2025-07-01 03:00:48.923 if eqi is None:
2025-07-01 03:00:48.928 eqi, eqj = i, j
2025-07-01 03:00:48.932 continue
2025-07-01 03:00:48.937 cruncher.set_seq1(ai)
2025-07-01 03:00:48.941 # computing similarity is expensive, so use the quick
2025-07-01 03:00:48.946 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:48.951 # compares by a factor of 3.
2025-07-01 03:00:48.955 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:48.960 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:48.964 # of the computation is cached by cruncher
2025-07-01 03:00:48.969 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:48.974 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:48.978 cruncher.ratio() > best_ratio:
2025-07-01 03:00:48.983 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:48.988 if best_ratio < cutoff:
2025-07-01 03:00:48.993 # no non-identical "pretty close" pair
2025-07-01 03:00:48.998 if eqi is None:
2025-07-01 03:00:49.003 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:49.007 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:49.012 return
2025-07-01 03:00:49.016 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:49.021 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:49.025 else:
2025-07-01 03:00:49.030 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:49.035 eqi = None
2025-07-01 03:00:49.039
2025-07-01 03:00:49.045 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:49.049 # identical
2025-07-01 03:00:49.054
2025-07-01 03:00:49.058 # pump out diffs from before the synch point
2025-07-01 03:00:49.063 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:49.067
2025-07-01 03:00:49.071 # do intraline marking on the synch pair
2025-07-01 03:00:49.076 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:49.080 if eqi is None:
2025-07-01 03:00:49.085 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:49.089 atags = btags = ""
2025-07-01 03:00:49.094 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:49.098 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:49.103 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:49.107 if tag == 'replace':
2025-07-01 03:00:49.112 atags += '^' * la
2025-07-01 03:00:49.116 btags += '^' * lb
2025-07-01 03:00:49.122 elif tag == 'delete':
2025-07-01 03:00:49.126 atags += '-' * la
2025-07-01 03:00:49.131 elif tag == 'insert':
2025-07-01 03:00:49.135 btags += '+' * lb
2025-07-01 03:00:49.140 elif tag == 'equal':
2025-07-01 03:00:49.144 atags += ' ' * la
2025-07-01 03:00:49.148 btags += ' ' * lb
2025-07-01 03:00:49.153 else:
2025-07-01 03:00:49.157 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:49.162 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:49.166 else:
2025-07-01 03:00:49.171 # the synch pair is identical
2025-07-01 03:00:49.175 yield '  ' + aelt
2025-07-01 03:00:49.180
2025-07-01 03:00:49.185 # pump out diffs from after the synch point
2025-07-01 03:00:49.190 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:49.194
2025-07-01 03:00:49.199 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:49.203 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:49.208
2025-07-01 03:00:49.213 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:49.217 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:49.221 alo = 118, ahi = 1101
2025-07-01 03:00:49.227 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:49.231 blo = 118, bhi = 1101
2025-07-01 03:00:49.235
2025-07-01 03:00:49.240 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:49.244 g = []
2025-07-01 03:00:49.249 if alo < ahi:
2025-07-01 03:00:49.253 if blo < bhi:
2025-07-01 03:00:49.258 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:49.262 else:
2025-07-01 03:00:49.267 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:49.271 elif blo < bhi:
2025-07-01 03:00:49.276 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:49.280
2025-07-01 03:00:49.285 >       yield from g
2025-07-01 03:00:49.289
2025-07-01 03:00:49.294 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:49.299 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:49.304
2025-07-01 03:00:49.309 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:49.314 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:49.320 alo = 118, ahi = 1101
2025-07-01 03:00:49.325 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:49.330 blo = 118, bhi = 1101
2025-07-01 03:00:49.338
2025-07-01 03:00:49.354 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:49.366 r"""
2025-07-01 03:00:49.374 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:49.386 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:49.402 synch point, and intraline difference marking is done on the
2025-07-01 03:00:49.410 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:49.426
2025-07-01 03:00:49.438 Example:
2025-07-01 03:00:49.450
2025-07-01 03:00:49.461 >>> d = Differ()
2025-07-01 03:00:49.474 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:49.486 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:49.499 >>> print(''.join(results), end="")
2025-07-01 03:00:49.513 - abcDefghiJkl
2025-07-01 03:00:49.537 + abcdefGhijkl
2025-07-01 03:00:49.565 """
2025-07-01 03:00:49.577
2025-07-01 03:00:49.593 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:49.609 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:49.622 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:49.642 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:49.650 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:49.662
2025-07-01 03:00:49.673 # search for the pair that matches best without being identical
2025-07-01 03:00:49.690 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:49.702 # on junk -- unless we have to)
2025-07-01 03:00:49.710 for j in range(blo, bhi):
2025-07-01 03:00:49.722 bj = b[j]
2025-07-01 03:00:49.730 cruncher.set_seq2(bj)
2025-07-01 03:00:49.742 for i in range(alo, ahi):
2025-07-01 03:00:49.754 ai = a[i]
2025-07-01 03:00:49.762 if ai == bj:
2025-07-01 03:00:49.774 if eqi is None:
2025-07-01 03:00:49.786 eqi, eqj = i, j
2025-07-01 03:00:49.794 continue
2025-07-01 03:00:49.806 cruncher.set_seq1(ai)
2025-07-01 03:00:49.817 # computing similarity is expensive, so use the quick
2025-07-01 03:00:49.830 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:49.842 # compares by a factor of 3.
2025-07-01 03:00:49.854 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:49.862 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:49.878 # of the computation is cached by cruncher
2025-07-01 03:00:49.890 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:49.906 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:49.922 cruncher.ratio() > best_ratio:
2025-07-01 03:00:49.930 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:49.942 if best_ratio < cutoff:
2025-07-01 03:00:49.957 # no non-identical "pretty close" pair
2025-07-01 03:00:49.966 if eqi is None:
2025-07-01 03:00:49.985 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:49.998 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:50.010 return
2025-07-01 03:00:50.026 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:50.038 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:50.049 else:
2025-07-01 03:00:50.058 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:50.070 eqi = None
2025-07-01 03:00:50.082
2025-07-01 03:00:50.090 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:50.102 # identical
2025-07-01 03:00:50.110
2025-07-01 03:00:50.126 # pump out diffs from before the synch point
2025-07-01 03:00:50.134 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:50.142
2025-07-01 03:00:50.153 # do intraline marking on the synch pair
2025-07-01 03:00:50.165 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:50.178 if eqi is None:
2025-07-01 03:00:50.186 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:50.196 atags = btags = ""
2025-07-01 03:00:50.205 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:50.218 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:50.228 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:50.235 if tag == 'replace':
2025-07-01 03:00:50.251 atags += '^' * la
2025-07-01 03:00:50.266 btags += '^' * lb
2025-07-01 03:00:50.275 elif tag == 'delete':
2025-07-01 03:00:50.294 atags += '-' * la
2025-07-01 03:00:50.310 elif tag == 'insert':
2025-07-01 03:00:50.315 btags += '+' * lb
2025-07-01 03:00:50.330 elif tag == 'equal':
2025-07-01 03:00:50.335 atags += ' ' * la
2025-07-01 03:00:50.349 btags += ' ' * lb
2025-07-01 03:00:50.365 else:
2025-07-01 03:00:50.374 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:50.389 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:50.401 else:
2025-07-01 03:00:50.411 # the synch pair is identical
2025-07-01 03:00:50.424 yield '  ' + aelt
2025-07-01 03:00:50.441
2025-07-01 03:00:50.456 # pump out diffs from after the synch point
2025-07-01 03:00:50.476 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:50.492
2025-07-01 03:00:50.513 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:50.522 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:50.537
2025-07-01 03:00:50.553 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:50.565 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:50.578 alo = 119, ahi = 1101
2025-07-01 03:00:50.586 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:50.594 blo = 119, bhi = 1101
2025-07-01 03:00:50.602
2025-07-01 03:00:50.614 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:50.626 g = []
2025-07-01 03:00:50.642 if alo < ahi:
2025-07-01 03:00:50.654 if blo < bhi:
2025-07-01 03:00:50.666 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:50.673 else:
2025-07-01 03:00:50.686 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:50.698 elif blo < bhi:
2025-07-01 03:00:50.710 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:50.718
2025-07-01 03:00:50.730 >       yield from g
2025-07-01 03:00:50.737
2025-07-01 03:00:50.754 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:50.762 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:50.774
2025-07-01 03:00:50.782 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:50.794 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:50.806 alo = 119, ahi = 1101
2025-07-01 03:00:50.822 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:50.830 blo = 119, bhi = 1101
2025-07-01 03:00:50.846
2025-07-01 03:00:50.862 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:50.878 r"""
2025-07-01 03:00:50.894 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:50.907 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:50.926 synch point, and intraline difference marking is done on the
2025-07-01 03:00:50.942 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:50.954
2025-07-01 03:00:50.965 Example:
2025-07-01 03:00:50.974
2025-07-01 03:00:50.994 >>> d = Differ()
2025-07-01 03:00:51.005 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:51.014 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:51.026 >>> print(''.join(results), end="")
2025-07-01 03:00:51.034 - abcDefghiJkl
2025-07-01 03:00:51.054 + abcdefGhijkl
2025-07-01 03:00:51.078 """
2025-07-01 03:00:51.090
2025-07-01 03:00:51.102 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:51.110 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:51.122 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:51.130 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:51.142 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:51.150
2025-07-01 03:00:51.158 # search for the pair that matches best without being identical
2025-07-01 03:00:51.169 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:51.185 # on junk -- unless we have to)
2025-07-01 03:00:51.194 for j in range(blo, bhi):
2025-07-01 03:00:51.206 bj = b[j]
2025-07-01 03:00:51.222 cruncher.set_seq2(bj)
2025-07-01 03:00:51.238 for i in range(alo, ahi):
2025-07-01 03:00:51.246 ai = a[i]
2025-07-01 03:00:51.254 if ai == bj:
2025-07-01 03:00:51.265 if eqi is None:
2025-07-01 03:00:51.282 eqi, eqj = i, j
2025-07-01 03:00:51.290 continue
2025-07-01 03:00:51.304 cruncher.set_seq1(ai)
2025-07-01 03:00:51.314 # computing similarity is expensive, so use the quick
2025-07-01 03:00:51.326 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:51.334 # compares by a factor of 3.
2025-07-01 03:00:51.346 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:51.355 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:51.370 # of the computation is cached by cruncher
2025-07-01 03:00:51.378 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:51.393 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:51.402 cruncher.ratio() > best_ratio:
2025-07-01 03:00:51.414 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:51.426 if best_ratio < cutoff:
2025-07-01 03:00:51.437 # no non-identical "pretty close" pair
2025-07-01 03:00:51.448 if eqi is None:
2025-07-01 03:00:51.460 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:51.474 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:51.482 return
2025-07-01 03:00:51.494 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:51.506 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:51.514 else:
2025-07-01 03:00:51.530 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:51.538 eqi = None
2025-07-01 03:00:51.550
2025-07-01 03:00:51.558 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:51.570 # identical
2025-07-01 03:00:51.578
2025-07-01 03:00:51.594 # pump out diffs from before the synch point
2025-07-01 03:00:51.602 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:51.618
2025-07-01 03:00:51.625 # do intraline marking on the synch pair
2025-07-01 03:00:51.636 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:51.650 if eqi is None:
2025-07-01 03:00:51.662 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:51.679 atags = btags = ""
2025-07-01 03:00:51.694 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:51.702 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:51.714 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:51.726 if tag == 'replace':
2025-07-01 03:00:51.738 atags += '^' * la
2025-07-01 03:00:51.750 btags += '^' * lb
2025-07-01 03:00:51.762 elif tag == 'delete':
2025-07-01 03:00:51.770 atags += '-' * la
2025-07-01 03:00:51.785 elif tag == 'insert':
2025-07-01 03:00:51.802 btags += '+' * lb
2025-07-01 03:00:51.810 elif tag == 'equal':
2025-07-01 03:00:51.822 atags += ' ' * la
2025-07-01 03:00:51.834 btags += ' ' * lb
2025-07-01 03:00:51.842 else:
2025-07-01 03:00:51.854 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:51.862 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:51.874 else:
2025-07-01 03:00:51.882 # the synch pair is identical
2025-07-01 03:00:51.894 yield '  ' + aelt
2025-07-01 03:00:51.906
2025-07-01 03:00:51.922 # pump out diffs from after the synch point
2025-07-01 03:00:51.934 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:51.946
2025-07-01 03:00:51.966 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:51.978 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:51.985
2025-07-01 03:00:51.998 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:52.010 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:52.018 alo = 120, ahi = 1101
2025-07-01 03:00:52.032 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:52.046 blo = 120, bhi = 1101
2025-07-01 03:00:52.055
2025-07-01 03:00:52.074 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:52.082 g = []
2025-07-01 03:00:52.098 if alo < ahi:
2025-07-01 03:00:52.106 if blo < bhi:
2025-07-01 03:00:52.118 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:52.130 else:
2025-07-01 03:00:52.137 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:52.154 elif blo < bhi:
2025-07-01 03:00:52.166 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:52.182
2025-07-01 03:00:52.194 >       yield from g
2025-07-01 03:00:52.203
2025-07-01 03:00:52.222 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:52.230 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:52.242
2025-07-01 03:00:52.250 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:52.262 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:52.274 alo = 120, ahi = 1101
2025-07-01 03:00:52.286 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:52.298 blo = 120, bhi = 1101
2025-07-01 03:00:52.309
2025-07-01 03:00:52.324 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:52.338 r"""
2025-07-01 03:00:52.346 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:52.357 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:52.374 synch point, and intraline difference marking is done on the
2025-07-01 03:00:52.386 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:52.400
2025-07-01 03:00:52.411 Example:
2025-07-01 03:00:52.426
2025-07-01 03:00:52.434 >>> d = Differ()
2025-07-01 03:00:52.446 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:52.458 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:52.474 >>> print(''.join(results), end="")
2025-07-01 03:00:52.486 - abcDefghiJkl
2025-07-01 03:00:52.502 + abcdefGhijkl
2025-07-01 03:00:52.522 """
2025-07-01 03:00:52.534
2025-07-01 03:00:52.542 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:52.562 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:52.570 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:52.579 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:52.590 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:52.598
2025-07-01 03:00:52.610 # search for the pair that matches best without being identical
2025-07-01 03:00:52.622 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:52.630 # on junk -- unless we have to)
2025-07-01 03:00:52.642 for j in range(blo, bhi):
2025-07-01 03:00:52.654 bj = b[j]
2025-07-01 03:00:52.662 cruncher.set_seq2(bj)
2025-07-01 03:00:52.674 for i in range(alo, ahi):
2025-07-01 03:00:52.682 ai = a[i]
2025-07-01 03:00:52.694 if ai == bj:
2025-07-01 03:00:52.706 if eqi is None:
2025-07-01 03:00:52.722 eqi, eqj = i, j
2025-07-01 03:00:52.734 continue
2025-07-01 03:00:52.746 cruncher.set_seq1(ai)
2025-07-01 03:00:52.758 # computing similarity is expensive, so use the quick
2025-07-01 03:00:52.770 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:52.780 # compares by a factor of 3.
2025-07-01 03:00:52.794 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:52.802 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:52.810 # of the computation is cached by cruncher
2025-07-01 03:00:52.822 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:52.830 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:52.841 cruncher.ratio() > best_ratio:
2025-07-01 03:00:52.852 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:52.866 if best_ratio < cutoff:
2025-07-01 03:00:52.882 # no non-identical "pretty close" pair
2025-07-01 03:00:52.894 if eqi is None:
2025-07-01 03:00:52.902 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:52.914 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:52.922 return
2025-07-01 03:00:52.934 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:52.946 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:52.954 else:
2025-07-01 03:00:52.963 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:52.970 eqi = None
2025-07-01 03:00:52.977
2025-07-01 03:00:52.994 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:53.002 # identical
2025-07-01 03:00:53.014
2025-07-01 03:00:53.026 # pump out diffs from before the synch point
2025-07-01 03:00:53.034 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:53.046
2025-07-01 03:00:53.052 # do intraline marking on the synch pair
2025-07-01 03:00:53.066 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:53.074 if eqi is None:
2025-07-01 03:00:53.086 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:53.102 atags = btags = ""
2025-07-01 03:00:53.110 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:53.122 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:53.134 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:53.142 if tag == 'replace':
2025-07-01 03:00:53.150 atags += '^' * la
2025-07-01 03:00:53.164 btags += '^' * lb
2025-07-01 03:00:53.178 elif tag == 'delete':
2025-07-01 03:00:53.195 atags += '-' * la
2025-07-01 03:00:53.210 elif tag == 'insert':
2025-07-01 03:00:53.219 btags += '+' * lb
2025-07-01 03:00:53.233 elif tag == 'equal':
2025-07-01 03:00:53.248 atags += ' ' * la
2025-07-01 03:00:53.262 btags += ' ' * lb
2025-07-01 03:00:53.274 else:
2025-07-01 03:00:53.282 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:53.298 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:53.309 else:
2025-07-01 03:00:53.317 # the synch pair is identical
2025-07-01 03:00:53.327 yield '  ' + aelt
2025-07-01 03:00:53.339
2025-07-01 03:00:53.351 # pump out diffs from after the synch point
2025-07-01 03:00:53.360 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:53.371
2025-07-01 03:00:53.383 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:53.396 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:53.406
2025-07-01 03:00:53.414 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:53.438 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:53.445 alo = 121, ahi = 1101
2025-07-01 03:00:53.462 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:53.470 blo = 121, bhi = 1101
2025-07-01 03:00:53.482
2025-07-01 03:00:53.489 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:53.502 g = []
2025-07-01 03:00:53.514 if alo < ahi:
2025-07-01 03:00:53.522 if blo < bhi:
2025-07-01 03:00:53.534 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:53.542 else:
2025-07-01 03:00:53.554 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:53.562 elif blo < bhi:
2025-07-01 03:00:53.574 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:53.584
2025-07-01 03:00:53.593 >       yield from g
2025-07-01 03:00:53.598
2025-07-01 03:00:53.605 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:53.611 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:53.618
2025-07-01 03:00:53.624 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:53.631 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:53.636 alo = 121, ahi = 1101
2025-07-01 03:00:53.643 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:53.649 blo = 121, bhi = 1101
2025-07-01 03:00:53.654
2025-07-01 03:00:53.660 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:53.666 r"""
2025-07-01 03:00:53.672 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:53.678 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:53.684 synch point, and intraline difference marking is done on the
2025-07-01 03:00:53.690 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:53.696
2025-07-01 03:00:53.700 Example:
2025-07-01 03:00:53.705
2025-07-01 03:00:53.709 >>> d = Differ()
2025-07-01 03:00:53.714 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:53.719 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:53.725 >>> print(''.join(results), end="")
2025-07-01 03:00:53.730 - abcDefghiJkl
2025-07-01 03:00:53.739 + abcdefGhijkl
2025-07-01 03:00:53.747 """
2025-07-01 03:00:53.752
2025-07-01 03:00:53.758 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:53.766 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:53.771 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:53.778 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:53.790 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:53.798
2025-07-01 03:00:53.806 # search for the pair that matches best without being identical
2025-07-01 03:00:53.814 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:53.822 # on junk -- unless we have to)
2025-07-01 03:00:53.833 for j in range(blo, bhi):
2025-07-01 03:00:53.858 bj = b[j]
2025-07-01 03:00:53.869 cruncher.set_seq2(bj)
2025-07-01 03:00:53.882 for i in range(alo, ahi):
2025-07-01 03:00:53.897 ai = a[i]
2025-07-01 03:00:53.910 if ai == bj:
2025-07-01 03:00:53.924 if eqi is None:
2025-07-01 03:00:53.932 eqi, eqj = i, j
2025-07-01 03:00:53.948 continue
2025-07-01 03:00:53.954 cruncher.set_seq1(ai)
2025-07-01 03:00:53.970 # computing similarity is expensive, so use the quick
2025-07-01 03:00:53.982 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:53.990 # compares by a factor of 3.
2025-07-01 03:00:53.998 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:54.010 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:54.018 # of the computation is cached by cruncher
2025-07-01 03:00:54.030 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:54.042 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:54.050 cruncher.ratio() > best_ratio:
2025-07-01 03:00:54.059 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:54.070 if best_ratio < cutoff:
2025-07-01 03:00:54.086 # no non-identical "pretty close" pair
2025-07-01 03:00:54.094 if eqi is None:
2025-07-01 03:00:54.102 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:54.113 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:54.130 return
2025-07-01 03:00:54.138 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:54.149 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:54.166 else:
2025-07-01 03:00:54.174 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:54.182 eqi = None
2025-07-01 03:00:54.197
2025-07-01 03:00:54.211 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:54.226 # identical
2025-07-01 03:00:54.234
2025-07-01 03:00:54.246 # pump out diffs from before the synch point
2025-07-01 03:00:54.254 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:54.266
2025-07-01 03:00:54.274 # do intraline marking on the synch pair
2025-07-01 03:00:54.286 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:54.294 if eqi is None:
2025-07-01 03:00:54.306 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:54.314 atags = btags = ""
2025-07-01 03:00:54.326 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:54.338 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:54.346 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:54.362 if tag == 'replace':
2025-07-01 03:00:54.374 atags += '^' * la
2025-07-01 03:00:54.386 btags += '^' * lb
2025-07-01 03:00:54.398 elif tag == 'delete':
2025-07-01 03:00:54.409 atags += '-' * la
2025-07-01 03:00:54.423 elif tag == 'insert':
2025-07-01 03:00:54.442 btags += '+' * lb
2025-07-01 03:00:54.450 elif tag == 'equal':
2025-07-01 03:00:54.466 atags += ' ' * la
2025-07-01 03:00:54.473 btags += ' ' * lb
2025-07-01 03:00:54.484 else:
2025-07-01 03:00:54.498 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:54.510 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:54.521 else:
2025-07-01 03:00:54.530 # the synch pair is identical
2025-07-01 03:00:54.545 yield '  ' + aelt
2025-07-01 03:00:54.554
2025-07-01 03:00:54.566 # pump out diffs from after the synch point
2025-07-01 03:00:54.582 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:54.591
2025-07-01 03:00:54.606 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:54.614 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:54.630
2025-07-01 03:00:54.638 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:54.650 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:54.665 alo = 122, ahi = 1101
2025-07-01 03:00:54.679 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:54.690 blo = 122, bhi = 1101
2025-07-01 03:00:54.705
2025-07-01 03:00:54.718 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:54.729 g = []
2025-07-01 03:00:54.740 if alo < ahi:
2025-07-01 03:00:54.753 if blo < bhi:
2025-07-01 03:00:54.765 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:54.777 else:
2025-07-01 03:00:54.789 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:54.801 elif blo < bhi:
2025-07-01 03:00:54.815 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:54.831
2025-07-01 03:00:54.842 >       yield from g
2025-07-01 03:00:54.850
2025-07-01 03:00:54.862 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:54.870 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:54.877
2025-07-01 03:00:54.886 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:54.898 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:54.910 alo = 122, ahi = 1101
2025-07-01 03:00:54.932 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:54.940 blo = 122, bhi = 1101
2025-07-01 03:00:54.957
2025-07-01 03:00:54.973 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:54.990 r"""
2025-07-01 03:00:54.998 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:55.011 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:55.024 synch point, and intraline difference marking is done on the
2025-07-01 03:00:55.034 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:55.053
2025-07-01 03:00:55.069 Example:
2025-07-01 03:00:55.079
2025-07-01 03:00:55.093 >>> d = Differ()
2025-07-01 03:00:55.107 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:55.118 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:55.126 >>> print(''.join(results), end="")
2025-07-01 03:00:55.146 - abcDefghiJkl
2025-07-01 03:00:55.166 + abcdefGhijkl
2025-07-01 03:00:55.186 """
2025-07-01 03:00:55.194
2025-07-01 03:00:55.206 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:55.218 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:55.230 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:55.242 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:55.258 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:55.269
2025-07-01 03:00:55.282 # search for the pair that matches best without being identical
2025-07-01 03:00:55.290 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:55.302 # on junk -- unless we have to)
2025-07-01 03:00:55.314 for j in range(blo, bhi):
2025-07-01 03:00:55.326 bj = b[j]
2025-07-01 03:00:55.338 cruncher.set_seq2(bj)
2025-07-01 03:00:55.346 for i in range(alo, ahi):
2025-07-01 03:00:55.358 ai = a[i]
2025-07-01 03:00:55.370 if ai == bj:
2025-07-01 03:00:55.378 if eqi is None:
2025-07-01 03:00:55.394 eqi, eqj = i, j
2025-07-01 03:00:55.406 continue
2025-07-01 03:00:55.414 cruncher.set_seq1(ai)
2025-07-01 03:00:55.426 # computing similarity is expensive, so use the quick
2025-07-01 03:00:55.438 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:55.446 # compares by a factor of 3.
2025-07-01 03:00:55.462 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:55.470 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:55.478 # of the computation is cached by cruncher
2025-07-01 03:00:55.490 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:55.501 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:55.510 cruncher.ratio() > best_ratio:
2025-07-01 03:00:55.522 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:55.538 if best_ratio < cutoff:
2025-07-01 03:00:55.546 # no non-identical "pretty close" pair
2025-07-01 03:00:55.558 if eqi is None:
2025-07-01 03:00:55.570 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:55.578 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:55.594 return
2025-07-01 03:00:55.606 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:55.618 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:55.634 else:
2025-07-01 03:00:55.640 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:55.656 eqi = None
2025-07-01 03:00:55.666
2025-07-01 03:00:55.678 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:55.686 # identical
2025-07-01 03:00:55.698
2025-07-01 03:00:55.710 # pump out diffs from before the synch point
2025-07-01 03:00:55.722 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:55.733
2025-07-01 03:00:55.742 # do intraline marking on the synch pair
2025-07-01 03:00:55.758 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:55.774 if eqi is None:
2025-07-01 03:00:55.786 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:55.794 atags = btags = ""
2025-07-01 03:00:55.806 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:55.822 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:55.830 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:55.842 if tag == 'replace':
2025-07-01 03:00:55.854 atags += '^' * la
2025-07-01 03:00:55.866 btags += '^' * lb
2025-07-01 03:00:55.874 elif tag == 'delete':
2025-07-01 03:00:55.886 atags += '-' * la
2025-07-01 03:00:55.894 elif tag == 'insert':
2025-07-01 03:00:55.906 btags += '+' * lb
2025-07-01 03:00:55.918 elif tag == 'equal':
2025-07-01 03:00:55.926 atags += ' ' * la
2025-07-01 03:00:55.936 btags += ' ' * lb
2025-07-01 03:00:55.950 else:
2025-07-01 03:00:55.958 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:55.970 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:55.982 else:
2025-07-01 03:00:55.994 # the synch pair is identical
2025-07-01 03:00:56.006 yield '  ' + aelt
2025-07-01 03:00:56.018
2025-07-01 03:00:56.030 # pump out diffs from after the synch point
2025-07-01 03:00:56.038 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:56.050
2025-07-01 03:00:56.058 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:56.070 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:56.086
2025-07-01 03:00:56.098 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:56.114 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:56.130 alo = 123, ahi = 1101
2025-07-01 03:00:56.142 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:56.154 blo = 123, bhi = 1101
2025-07-01 03:00:56.166
2025-07-01 03:00:56.178 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:56.186 g = []
2025-07-01 03:00:56.194 if alo < ahi:
2025-07-01 03:00:56.206 if blo < bhi:
2025-07-01 03:00:56.214 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:56.226 else:
2025-07-01 03:00:56.238 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:56.254 elif blo < bhi:
2025-07-01 03:00:56.270 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:56.286
2025-07-01 03:00:56.298 >       yield from g
2025-07-01 03:00:56.310
2025-07-01 03:00:56.320 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:56.330 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:56.341
2025-07-01 03:00:56.349 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:56.369 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:56.378 alo = 123, ahi = 1101
2025-07-01 03:00:56.393 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:56.406 blo = 123, bhi = 1101
2025-07-01 03:00:56.418
2025-07-01 03:00:56.426 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:56.438 r"""
2025-07-01 03:00:56.452 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:56.474 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:56.486 synch point, and intraline difference marking is done on the
2025-07-01 03:00:56.497 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:56.510
2025-07-01 03:00:56.518 Example:
2025-07-01 03:00:56.532
2025-07-01 03:00:56.542 >>> d = Differ()
2025-07-01 03:00:56.554 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:56.566 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:56.574 >>> print(''.join(results), end="")
2025-07-01 03:00:56.590 - abcDefghiJkl
2025-07-01 03:00:56.610 + abcdefGhijkl
2025-07-01 03:00:56.646 """
2025-07-01 03:00:56.662
2025-07-01 03:00:56.670 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:56.678 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:56.690 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:56.702 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:56.710 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:56.722
2025-07-01 03:00:56.734 # search for the pair that matches best without being identical
2025-07-01 03:00:56.739 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:56.749 # on junk -- unless we have to)
2025-07-01 03:00:56.762 for j in range(blo, bhi):
2025-07-01 03:00:56.769 bj = b[j]
2025-07-01 03:00:56.782 cruncher.set_seq2(bj)
2025-07-01 03:00:56.790 for i in range(alo, ahi):
2025-07-01 03:00:56.803 ai = a[i]
2025-07-01 03:00:56.814 if ai == bj:
2025-07-01 03:00:56.826 if eqi is None:
2025-07-01 03:00:56.840 eqi, eqj = i, j
2025-07-01 03:00:56.850 continue
2025-07-01 03:00:56.865 cruncher.set_seq1(ai)
2025-07-01 03:00:56.873 # computing similarity is expensive, so use the quick
2025-07-01 03:00:56.886 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:56.894 # compares by a factor of 3.
2025-07-01 03:00:56.914 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:56.930 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:56.938 # of the computation is cached by cruncher
2025-07-01 03:00:56.951 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:56.962 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:56.976 cruncher.ratio() > best_ratio:
2025-07-01 03:00:56.986 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:56.998 if best_ratio < cutoff:
2025-07-01 03:00:57.010 # no non-identical "pretty close" pair
2025-07-01 03:00:57.022 if eqi is None:
2025-07-01 03:00:57.038 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:57.046 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:57.054 return
2025-07-01 03:00:57.066 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:57.073 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:57.086 else:
2025-07-01 03:00:57.094 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:57.109 eqi = None
2025-07-01 03:00:57.118
2025-07-01 03:00:57.130 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:57.138 # identical
2025-07-01 03:00:57.150
2025-07-01 03:00:57.158 # pump out diffs from before the synch point
2025-07-01 03:00:57.170 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:57.178
2025-07-01 03:00:57.190 # do intraline marking on the synch pair
2025-07-01 03:00:57.198 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:57.210 if eqi is None:
2025-07-01 03:00:57.222 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:57.230 atags = btags = ""
2025-07-01 03:00:57.238 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:57.254 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:57.262 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:57.274 if tag == 'replace':
2025-07-01 03:00:57.288 atags += '^' * la
2025-07-01 03:00:57.296 btags += '^' * lb
2025-07-01 03:00:57.312 elif tag == 'delete':
2025-07-01 03:00:57.322 atags += '-' * la
2025-07-01 03:00:57.334 elif tag == 'insert':
2025-07-01 03:00:57.342 btags += '+' * lb
2025-07-01 03:00:57.352 elif tag == 'equal':
2025-07-01 03:00:57.361 atags += ' ' * la
2025-07-01 03:00:57.373 btags += ' ' * lb
2025-07-01 03:00:57.386 else:
2025-07-01 03:00:57.398 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:57.410 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:57.426 else:
2025-07-01 03:00:57.438 # the synch pair is identical
2025-07-01 03:00:57.449 yield '  ' + aelt
2025-07-01 03:00:57.458
2025-07-01 03:00:57.470 # pump out diffs from after the synch point
2025-07-01 03:00:57.481 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:57.490
2025-07-01 03:00:57.496 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:57.501 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:57.506
2025-07-01 03:00:57.511 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:57.517 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:57.522 alo = 124, ahi = 1101
2025-07-01 03:00:57.527 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:57.532 blo = 124, bhi = 1101
2025-07-01 03:00:57.536
2025-07-01 03:00:57.542 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:57.546 g = []
2025-07-01 03:00:57.551 if alo < ahi:
2025-07-01 03:00:57.556 if blo < bhi:
2025-07-01 03:00:57.560 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:57.565 else:
2025-07-01 03:00:57.570 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:57.574 elif blo < bhi:
2025-07-01 03:00:57.579 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:57.583
2025-07-01 03:00:57.588 >       yield from g
2025-07-01 03:00:57.592
2025-07-01 03:00:57.597 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:57.601 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:57.606
2025-07-01 03:00:57.610 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:57.615 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:57.621 alo = 124, ahi = 1101
2025-07-01 03:00:57.628 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:57.633 blo = 124, bhi = 1101
2025-07-01 03:00:57.637
2025-07-01 03:00:57.642 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:57.646 r"""
2025-07-01 03:00:57.651 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:57.656 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:57.660 synch point, and intraline difference marking is done on the
2025-07-01 03:00:57.665 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:57.670
2025-07-01 03:00:57.674 Example:
2025-07-01 03:00:57.679
2025-07-01 03:00:57.683 >>> d = Differ()
2025-07-01 03:00:57.688 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:57.692 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:57.697 >>> print(''.join(results), end="")
2025-07-01 03:00:57.701 - abcDefghiJkl
2025-07-01 03:00:57.710 + abcdefGhijkl
2025-07-01 03:00:57.719 """
2025-07-01 03:00:57.723
2025-07-01 03:00:57.728 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:57.732 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:57.738 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:57.743 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:57.747 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:57.752
2025-07-01 03:00:57.757 # search for the pair that matches best without being identical
2025-07-01 03:00:57.761 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:57.766 # on junk -- unless we have to)
2025-07-01 03:00:57.770 for j in range(blo, bhi):
2025-07-01 03:00:57.775 bj = b[j]
2025-07-01 03:00:57.779 cruncher.set_seq2(bj)
2025-07-01 03:00:57.784 for i in range(alo, ahi):
2025-07-01 03:00:57.788 ai = a[i]
2025-07-01 03:00:57.793 if ai == bj:
2025-07-01 03:00:57.797 if eqi is None:
2025-07-01 03:00:57.802 eqi, eqj = i, j
2025-07-01 03:00:57.806 continue
2025-07-01 03:00:57.811 cruncher.set_seq1(ai)
2025-07-01 03:00:57.815 # computing similarity is expensive, so use the quick
2025-07-01 03:00:57.820 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:57.824 # compares by a factor of 3.
2025-07-01 03:00:57.829 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:57.833 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:57.838 # of the computation is cached by cruncher
2025-07-01 03:00:57.842 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:57.847 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:57.851 cruncher.ratio() > best_ratio:
2025-07-01 03:00:57.856 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:57.860 if best_ratio < cutoff:
2025-07-01 03:00:57.865 # no non-identical "pretty close" pair
2025-07-01 03:00:57.869 if eqi is None:
2025-07-01 03:00:57.874 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:57.878 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:57.883 return
2025-07-01 03:00:57.887 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:57.892 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:57.896 else:
2025-07-01 03:00:57.901 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:57.905 eqi = None
2025-07-01 03:00:57.910
2025-07-01 03:00:57.914 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:57.919 # identical
2025-07-01 03:00:57.924
2025-07-01 03:00:57.928 # pump out diffs from before the synch point
2025-07-01 03:00:57.933 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:57.938
2025-07-01 03:00:57.942 # do intraline marking on the synch pair
2025-07-01 03:00:57.947 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:57.951 if eqi is None:
2025-07-01 03:00:57.956 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:57.961 atags = btags = ""
2025-07-01 03:00:57.965 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:57.970 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:57.975 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:57.986 if tag == 'replace':
2025-07-01 03:00:57.995 atags += '^' * la
2025-07-01 03:00:58.011 btags += '^' * lb
2025-07-01 03:00:58.026 elif tag == 'delete':
2025-07-01 03:00:58.036 atags += '-' * la
2025-07-01 03:00:58.050 elif tag == 'insert':
2025-07-01 03:00:58.058 btags += '+' * lb
2025-07-01 03:00:58.073 elif tag == 'equal':
2025-07-01 03:00:58.082 atags += ' ' * la
2025-07-01 03:00:58.094 btags += ' ' * lb
2025-07-01 03:00:58.106 else:
2025-07-01 03:00:58.114 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:58.126 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:58.138 else:
2025-07-01 03:00:58.146 # the synch pair is identical
2025-07-01 03:00:58.158 yield '  ' + aelt
2025-07-01 03:00:58.174
2025-07-01 03:00:58.190 # pump out diffs from after the synch point
2025-07-01 03:00:58.198 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:58.206
2025-07-01 03:00:58.218 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:58.226 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:58.238
2025-07-01 03:00:58.250 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:58.262 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:58.270 alo = 125, ahi = 1101
2025-07-01 03:00:58.286 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:58.298 blo = 125, bhi = 1101
2025-07-01 03:00:58.306
2025-07-01 03:00:58.318 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:58.330 g = []
2025-07-01 03:00:58.338 if alo < ahi:
2025-07-01 03:00:58.350 if blo < bhi:
2025-07-01 03:00:58.362 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:58.378 else:
2025-07-01 03:00:58.394 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:58.406 elif blo < bhi:
2025-07-01 03:00:58.414 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:58.426
2025-07-01 03:00:58.442 >       yield from g
2025-07-01 03:00:58.454
2025-07-01 03:00:58.462 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:58.477 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:58.490
2025-07-01 03:00:58.504 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:58.514 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:58.526 alo = 125, ahi = 1101
2025-07-01 03:00:58.542 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:58.558 blo = 125, bhi = 1101
2025-07-01 03:00:58.571
2025-07-01 03:00:58.590 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:58.598 r"""
2025-07-01 03:00:58.611 When replacing one block of lines with another, search the blocks
2025-07-01 03:00:58.626 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:00:58.634 synch point, and intraline difference marking is done on the
2025-07-01 03:00:58.648 similar pair. Lots of work, but often worth it.
2025-07-01 03:00:58.658
2025-07-01 03:00:58.669 Example:
2025-07-01 03:00:58.678
2025-07-01 03:00:58.693 >>> d = Differ()
2025-07-01 03:00:58.702 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:00:58.714 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:00:58.726 >>> print(''.join(results), end="")
2025-07-01 03:00:58.740 - abcDefghiJkl
2025-07-01 03:00:58.766 + abcdefGhijkl
2025-07-01 03:00:58.786 """
2025-07-01 03:00:58.794
2025-07-01 03:00:58.806 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:00:58.818 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:00:58.826 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:00:58.838 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:00:58.850 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:00:58.862
2025-07-01 03:00:58.878 # search for the pair that matches best without being identical
2025-07-01 03:00:58.886 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:00:58.894 # on junk -- unless we have to)
2025-07-01 03:00:58.906 for j in range(blo, bhi):
2025-07-01 03:00:58.922 bj = b[j]
2025-07-01 03:00:58.933 cruncher.set_seq2(bj)
2025-07-01 03:00:58.942 for i in range(alo, ahi):
2025-07-01 03:00:58.963 ai = a[i]
2025-07-01 03:00:58.983 if ai == bj:
2025-07-01 03:00:58.998 if eqi is None:
2025-07-01 03:00:59.010 eqi, eqj = i, j
2025-07-01 03:00:59.018 continue
2025-07-01 03:00:59.026 cruncher.set_seq1(ai)
2025-07-01 03:00:59.037 # computing similarity is expensive, so use the quick
2025-07-01 03:00:59.054 # upper bounds first -- have seen this speed up messy
2025-07-01 03:00:59.063 # compares by a factor of 3.
2025-07-01 03:00:59.073 # note that ratio() is only expensive to compute the first
2025-07-01 03:00:59.086 # time it's called on a sequence pair; the expensive part
2025-07-01 03:00:59.098 # of the computation is cached by cruncher
2025-07-01 03:00:59.114 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:00:59.126 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:00:59.149 cruncher.ratio() > best_ratio:
2025-07-01 03:00:59.158 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:00:59.166 if best_ratio < cutoff:
2025-07-01 03:00:59.181 # no non-identical "pretty close" pair
2025-07-01 03:00:59.193 if eqi is None:
2025-07-01 03:00:59.209 # no identical pair either -- treat it as a straight replace
2025-07-01 03:00:59.218 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:59.237 return
2025-07-01 03:00:59.249 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:00:59.265 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:00:59.278 else:
2025-07-01 03:00:59.295 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:00:59.302 eqi = None
2025-07-01 03:00:59.310
2025-07-01 03:00:59.321 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:00:59.338 # identical
2025-07-01 03:00:59.349
2025-07-01 03:00:59.366 # pump out diffs from before the synch point
2025-07-01 03:00:59.378 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:00:59.393
2025-07-01 03:00:59.405 # do intraline marking on the synch pair
2025-07-01 03:00:59.416 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:00:59.428 if eqi is None:
2025-07-01 03:00:59.446 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:00:59.454 atags = btags = ""
2025-07-01 03:00:59.464 cruncher.set_seqs(aelt, belt)
2025-07-01 03:00:59.476 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:00:59.490 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:00:59.498 if tag == 'replace':
2025-07-01 03:00:59.514 atags += '^' * la
2025-07-01 03:00:59.521 btags += '^' * lb
2025-07-01 03:00:59.538 elif tag == 'delete':
2025-07-01 03:00:59.546 atags += '-' * la
2025-07-01 03:00:59.558 elif tag == 'insert':
2025-07-01 03:00:59.574 btags += '+' * lb
2025-07-01 03:00:59.582 elif tag == 'equal':
2025-07-01 03:00:59.590 atags += ' ' * la
2025-07-01 03:00:59.602 btags += ' ' * lb
2025-07-01 03:00:59.618 else:
2025-07-01 03:00:59.626 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:00:59.638 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:00:59.654 else:
2025-07-01 03:00:59.666 # the synch pair is identical
2025-07-01 03:00:59.678 yield '  ' + aelt
2025-07-01 03:00:59.688
2025-07-01 03:00:59.701 # pump out diffs from after the synch point
2025-07-01 03:00:59.714 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:00:59.722
2025-07-01 03:00:59.738 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:00:59.751 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:00:59.762
2025-07-01 03:00:59.774 self = <difflib.Differ object at [hex]>
2025-07-01 03:00:59.786 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:00:59.794 alo = 126, ahi = 1101
2025-07-01 03:00:59.802 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:00:59.822 blo = 126, bhi = 1101
2025-07-01 03:00:59.834
2025-07-01 03:00:59.842 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:00:59.854 g = []
2025-07-01 03:00:59.862 if alo < ahi:
2025-07-01 03:00:59.874 if blo < bhi:
2025-07-01 03:00:59.882 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:00:59.898 else:
2025-07-01 03:00:59.907 g = self._dump('-', a, alo, ahi)
2025-07-01 03:00:59.920 elif blo < bhi:
2025-07-01 03:00:59.933 g = self._dump('+', b, blo, bhi)
2025-07-01 03:00:59.946
2025-07-01 03:00:59.954 >       yield from g
2025-07-01 03:00:59.966
2025-07-01 03:00:59.977 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:00:59.989 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:00.010
2025-07-01 03:01:00.026 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:00.050 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:00.062 alo = 126, ahi = 1101
2025-07-01 03:01:00.074 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:00.082 blo = 126, bhi = 1101
2025-07-01 03:01:00.098
2025-07-01 03:01:00.110 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:00.118 r"""
2025-07-01 03:01:00.130 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:00.142 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:00.154 synch point, and intraline difference marking is done on the
2025-07-01 03:01:00.166 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:00.174
2025-07-01 03:01:00.186 Example:
2025-07-01 03:01:00.194
2025-07-01 03:01:00.201 >>> d = Differ()
2025-07-01 03:01:00.208 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:00.222 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:00.237 >>> print(''.join(results), end="")
2025-07-01 03:01:00.249 - abcDefghiJkl
2025-07-01 03:01:00.261 + abcdefGhijkl
2025-07-01 03:01:00.273 """
2025-07-01 03:01:00.279
2025-07-01 03:01:00.285 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:00.291 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:00.296 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:00.303 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:00.309 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:00.314
2025-07-01 03:01:00.319 # search for the pair that matches best without being identical
2025-07-01 03:01:00.325 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:00.332 # on junk -- unless we have to)
2025-07-01 03:01:00.339 for j in range(blo, bhi):
2025-07-01 03:01:00.344 bj = b[j]
2025-07-01 03:01:00.349 cruncher.set_seq2(bj)
2025-07-01 03:01:00.355 for i in range(alo, ahi):
2025-07-01 03:01:00.361 ai = a[i]
2025-07-01 03:01:00.366 if ai == bj:
2025-07-01 03:01:00.373 if eqi is None:
2025-07-01 03:01:00.377 eqi, eqj = i, j
2025-07-01 03:01:00.382 continue
2025-07-01 03:01:00.386 cruncher.set_seq1(ai)
2025-07-01 03:01:00.391 # computing similarity is expensive, so use the quick
2025-07-01 03:01:00.396 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:00.400 # compares by a factor of 3.
2025-07-01 03:01:00.405 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:00.409 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:00.414 # of the computation is cached by cruncher
2025-07-01 03:01:00.418 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:00.423 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:00.427 cruncher.ratio() > best_ratio:
2025-07-01 03:01:00.432 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:00.436 if best_ratio < cutoff:
2025-07-01 03:01:00.441 # no non-identical "pretty close" pair
2025-07-01 03:01:00.445 if eqi is None:
2025-07-01 03:01:00.450 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:00.454 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:00.459 return
2025-07-01 03:01:00.463 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:00.468 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:00.472 else:
2025-07-01 03:01:00.477 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:00.481 eqi = None
2025-07-01 03:01:00.485
2025-07-01 03:01:00.489 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:00.494 # identical
2025-07-01 03:01:00.498
2025-07-01 03:01:00.503 # pump out diffs from before the synch point
2025-07-01 03:01:00.507 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:00.512
2025-07-01 03:01:00.516 # do intraline marking on the synch pair
2025-07-01 03:01:00.521 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:00.525 if eqi is None:
2025-07-01 03:01:00.530 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:00.534 atags = btags = ""
2025-07-01 03:01:00.539 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:00.543 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:00.548 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:00.552 if tag == 'replace':
2025-07-01 03:01:00.557 atags += '^' * la
2025-07-01 03:01:00.561 btags += '^' * lb
2025-07-01 03:01:00.566 elif tag == 'delete':
2025-07-01 03:01:00.570 atags += '-' * la
2025-07-01 03:01:00.575 elif tag == 'insert':
2025-07-01 03:01:00.579 btags += '+' * lb
2025-07-01 03:01:00.583 elif tag == 'equal':
2025-07-01 03:01:00.587 atags += ' ' * la
2025-07-01 03:01:00.592 btags += ' ' * lb
2025-07-01 03:01:00.596 else:
2025-07-01 03:01:00.601 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:00.605 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:00.609 else:
2025-07-01 03:01:00.614 # the synch pair is identical
2025-07-01 03:01:00.618 yield '  ' + aelt
2025-07-01 03:01:00.623
2025-07-01 03:01:00.627 # pump out diffs from after the synch point
2025-07-01 03:01:00.639 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:00.654
2025-07-01 03:01:00.666 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:00.677 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:00.684
2025-07-01 03:01:00.708 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:00.717 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:00.730 alo = 127, ahi = 1101
2025-07-01 03:01:00.746 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:00.754 blo = 127, bhi = 1101
2025-07-01 03:01:00.768
2025-07-01 03:01:00.786 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:00.794 g = []
2025-07-01 03:01:00.806 if alo < ahi:
2025-07-01 03:01:00.814 if blo < bhi:
2025-07-01 03:01:00.830 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:00.842 else:
2025-07-01 03:01:00.850 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:00.862 elif blo < bhi:
2025-07-01 03:01:00.870 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:00.886
2025-07-01 03:01:00.898 >       yield from g
2025-07-01 03:01:00.906
2025-07-01 03:01:00.921 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:00.934 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:00.949
2025-07-01 03:01:00.961 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:00.974 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:00.994 alo = 127, ahi = 1101
2025-07-01 03:01:01.006 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:01.014 blo = 127, bhi = 1101
2025-07-01 03:01:01.026
2025-07-01 03:01:01.042 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:01.050 r"""
2025-07-01 03:01:01.064 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:01.086 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:01.094 synch point, and intraline difference marking is done on the
2025-07-01 03:01:01.106 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:01.114
2025-07-01 03:01:01.122 Example:
2025-07-01 03:01:01.138
2025-07-01 03:01:01.158 >>> d = Differ()
2025-07-01 03:01:01.165 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:01.178 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:01.186 >>> print(''.join(results), end="")
2025-07-01 03:01:01.199 - abcDefghiJkl
2025-07-01 03:01:01.222 + abcdefGhijkl
2025-07-01 03:01:01.254 """
2025-07-01 03:01:01.266
2025-07-01 03:01:01.276 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:01.290 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:01.304 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:01.320 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:01.330 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:01.338
2025-07-01 03:01:01.350 # search for the pair that matches best without being identical
2025-07-01 03:01:01.366 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:01.378 # on junk -- unless we have to)
2025-07-01 03:01:01.390 for j in range(blo, bhi):
2025-07-01 03:01:01.401 bj = b[j]
2025-07-01 03:01:01.410 cruncher.set_seq2(bj)
2025-07-01 03:01:01.422 for i in range(alo, ahi):
2025-07-01 03:01:01.441 ai = a[i]
2025-07-01 03:01:01.450 if ai == bj:
2025-07-01 03:01:01.458 if eqi is None:
2025-07-01 03:01:01.470 eqi, eqj = i, j
2025-07-01 03:01:01.482 continue
2025-07-01 03:01:01.494 cruncher.set_seq1(ai)
2025-07-01 03:01:01.505 # computing similarity is expensive, so use the quick
2025-07-01 03:01:01.522 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:01.533 # compares by a factor of 3.
2025-07-01 03:01:01.549 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:01.558 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:01.570 # of the computation is cached by cruncher
2025-07-01 03:01:01.578 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:01.590 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:01.598 cruncher.ratio() > best_ratio:
2025-07-01 03:01:01.614 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:01.625 if best_ratio < cutoff:
2025-07-01 03:01:01.650 # no non-identical "pretty close" pair
2025-07-01 03:01:01.658 if eqi is None:
2025-07-01 03:01:01.670 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:01.681 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:01.697 return
2025-07-01 03:01:01.709 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:01.722 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:01.730 else:
2025-07-01 03:01:01.742 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:01.749 eqi = None
2025-07-01 03:01:01.765
2025-07-01 03:01:01.774 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:01.790 # identical
2025-07-01 03:01:01.798
2025-07-01 03:01:01.810 # pump out diffs from before the synch point
2025-07-01 03:01:01.820 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:01.830
2025-07-01 03:01:01.846 # do intraline marking on the synch pair
2025-07-01 03:01:01.854 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:01.870 if eqi is None:
2025-07-01 03:01:01.883 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:01.895 atags = btags = ""
2025-07-01 03:01:01.914 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:01.925 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:01.937 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:01.948 if tag == 'replace':
2025-07-01 03:01:01.962 atags += '^' * la
2025-07-01 03:01:01.974 btags += '^' * lb
2025-07-01 03:01:01.989 elif tag == 'delete':
2025-07-01 03:01:02.001 atags += '-' * la
2025-07-01 03:01:02.014 elif tag == 'insert':
2025-07-01 03:01:02.022 btags += '+' * lb
2025-07-01 03:01:02.038 elif tag == 'equal':
2025-07-01 03:01:02.047 atags += ' ' * la
2025-07-01 03:01:02.060 btags += ' ' * lb
2025-07-01 03:01:02.070 else:
2025-07-01 03:01:02.081 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:02.092 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:02.110 else:
2025-07-01 03:01:02.125 # the synch pair is identical
2025-07-01 03:01:02.141 yield '  ' + aelt
2025-07-01 03:01:02.151
2025-07-01 03:01:02.165 # pump out diffs from after the synch point
2025-07-01 03:01:02.182 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:02.193
2025-07-01 03:01:02.209 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:02.225 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:02.237
2025-07-01 03:01:02.249 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:02.263 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:02.277 alo = 128, ahi = 1101
2025-07-01 03:01:02.293 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:02.309 blo = 128, bhi = 1101
2025-07-01 03:01:02.322
2025-07-01 03:01:02.338 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:02.344 g = []
2025-07-01 03:01:02.358 if alo < ahi:
2025-07-01 03:01:02.366 if blo < bhi:
2025-07-01 03:01:02.386 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:02.394 else:
2025-07-01 03:01:02.407 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:02.418 elif blo < bhi:
2025-07-01 03:01:02.434 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:02.442
2025-07-01 03:01:02.454 >       yield from g
2025-07-01 03:01:02.466
2025-07-01 03:01:02.478 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:02.490 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:02.502
2025-07-01 03:01:02.516 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:02.526 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:02.538 alo = 128, ahi = 1101
2025-07-01 03:01:02.550 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:02.566 blo = 128, bhi = 1101
2025-07-01 03:01:02.575
2025-07-01 03:01:02.592 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:02.609 r"""
2025-07-01 03:01:02.625 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:02.634 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:02.646 synch point, and intraline difference marking is done on the
2025-07-01 03:01:02.658 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:02.666
2025-07-01 03:01:02.678 Example:
2025-07-01 03:01:02.694
2025-07-01 03:01:02.710 >>> d = Differ()
2025-07-01 03:01:02.722 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:02.734 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:02.750 >>> print(''.join(results), end="")
2025-07-01 03:01:02.758 - abcDefghiJkl
2025-07-01 03:01:02.778 + abcdefGhijkl
2025-07-01 03:01:02.798 """
2025-07-01 03:01:02.810
2025-07-01 03:01:02.825 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:02.834 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:02.845 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:02.858 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:02.870 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:02.886
2025-07-01 03:01:02.897 # search for the pair that matches best without being identical
2025-07-01 03:01:02.922 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:02.938 # on junk -- unless we have to)
2025-07-01 03:01:02.950 for j in range(blo, bhi):
2025-07-01 03:01:02.958 bj = b[j]
2025-07-01 03:01:02.974 cruncher.set_seq2(bj)
2025-07-01 03:01:02.985 for i in range(alo, ahi):
2025-07-01 03:01:02.995 ai = a[i]
2025-07-01 03:01:03.014 if ai == bj:
2025-07-01 03:01:03.022 if eqi is None:
2025-07-01 03:01:03.038 eqi, eqj = i, j
2025-07-01 03:01:03.054 continue
2025-07-01 03:01:03.062 cruncher.set_seq1(ai)
2025-07-01 03:01:03.078 # computing similarity is expensive, so use the quick
2025-07-01 03:01:03.090 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:03.101 # compares by a factor of 3.
2025-07-01 03:01:03.122 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:03.134 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:03.146 # of the computation is cached by cruncher
2025-07-01 03:01:03.162 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:03.174 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:03.188 cruncher.ratio() > best_ratio:
2025-07-01 03:01:03.198 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:03.210 if best_ratio < cutoff:
2025-07-01 03:01:03.218 # no non-identical "pretty close" pair
2025-07-01 03:01:03.230 if eqi is None:
2025-07-01 03:01:03.238 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:03.254 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:03.272 return
2025-07-01 03:01:03.277 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:03.290 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:03.299 else:
2025-07-01 03:01:03.317 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:03.328 eqi = None
2025-07-01 03:01:03.337
2025-07-01 03:01:03.351 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:03.370 # identical
2025-07-01 03:01:03.377
2025-07-01 03:01:03.386 # pump out diffs from before the synch point
2025-07-01 03:01:03.406 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:03.414
2025-07-01 03:01:03.430 # do intraline marking on the synch pair
2025-07-01 03:01:03.446 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:03.458 if eqi is None:
2025-07-01 03:01:03.464 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:03.478 atags = btags = ""
2025-07-01 03:01:03.486 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:03.498 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:03.506 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:03.518 if tag == 'replace':
2025-07-01 03:01:03.530 atags += '^' * la
2025-07-01 03:01:03.542 btags += '^' * lb
2025-07-01 03:01:03.550 elif tag == 'delete':
2025-07-01 03:01:03.562 atags += '-' * la
2025-07-01 03:01:03.574 elif tag == 'insert':
2025-07-01 03:01:03.586 btags += '+' * lb
2025-07-01 03:01:03.596 elif tag == 'equal':
2025-07-01 03:01:03.610 atags += ' ' * la
2025-07-01 03:01:03.622 btags += ' ' * lb
2025-07-01 03:01:03.634 else:
2025-07-01 03:01:03.650 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:03.662 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:03.674 else:
2025-07-01 03:01:03.690 # the synch pair is identical
2025-07-01 03:01:03.698 yield '  ' + aelt
2025-07-01 03:01:03.714
2025-07-01 03:01:03.726 # pump out diffs from after the synch point
2025-07-01 03:01:03.738 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:03.754
2025-07-01 03:01:03.770 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:03.782 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:03.794
2025-07-01 03:01:03.811 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:03.822 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:03.830 alo = 129, ahi = 1101
2025-07-01 03:01:03.844 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:03.858 blo = 129, bhi = 1101
2025-07-01 03:01:03.866
2025-07-01 03:01:03.882 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:03.894 g = []
2025-07-01 03:01:03.906 if alo < ahi:
2025-07-01 03:01:03.916 if blo < bhi:
2025-07-01 03:01:03.930 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:03.938 else:
2025-07-01 03:01:03.946 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:03.958 elif blo < bhi:
2025-07-01 03:01:03.964 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:03.969
2025-07-01 03:01:03.974 >       yield from g
2025-07-01 03:01:03.978
2025-07-01 03:01:03.983 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:03.988 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:03.992
2025-07-01 03:01:03.997 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:04.002 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:04.007 alo = 129, ahi = 1101
2025-07-01 03:01:04.013 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:04.018 blo = 129, bhi = 1101
2025-07-01 03:01:04.023
2025-07-01 03:01:04.028 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:04.034 r"""
2025-07-01 03:01:04.039 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:04.044 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:04.049 synch point, and intraline difference marking is done on the
2025-07-01 03:01:04.054 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:04.059
2025-07-01 03:01:04.065 Example:
2025-07-01 03:01:04.070
2025-07-01 03:01:04.075 >>> d = Differ()
2025-07-01 03:01:04.083 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:04.096 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:04.106 >>> print(''.join(results), end="")
2025-07-01 03:01:04.122 - abcDefghiJkl
2025-07-01 03:01:04.138 + abcdefGhijkl
2025-07-01 03:01:04.169 """
2025-07-01 03:01:04.185
2025-07-01 03:01:04.201 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:04.213 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:04.233 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:04.242 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:04.254 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:04.262
2025-07-01 03:01:04.274 # search for the pair that matches best without being identical
2025-07-01 03:01:04.286 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:04.298 # on junk -- unless we have to)
2025-07-01 03:01:04.310 for j in range(blo, bhi):
2025-07-01 03:01:04.318 bj = b[j]
2025-07-01 03:01:04.330 cruncher.set_seq2(bj)
2025-07-01 03:01:04.342 for i in range(alo, ahi):
2025-07-01 03:01:04.350 ai = a[i]
2025-07-01 03:01:04.363 if ai == bj:
2025-07-01 03:01:04.378 if eqi is None:
2025-07-01 03:01:04.390 eqi, eqj = i, j
2025-07-01 03:01:04.400 continue
2025-07-01 03:01:04.418 cruncher.set_seq1(ai)
2025-07-01 03:01:04.430 # computing similarity is expensive, so use the quick
2025-07-01 03:01:04.438 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:04.450 # compares by a factor of 3.
2025-07-01 03:01:04.462 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:04.474 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:04.486 # of the computation is cached by cruncher
2025-07-01 03:01:04.498 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:04.506 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:04.518 cruncher.ratio() > best_ratio:
2025-07-01 03:01:04.534 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:04.542 if best_ratio < cutoff:
2025-07-01 03:01:04.550 # no non-identical "pretty close" pair
2025-07-01 03:01:04.562 if eqi is None:
2025-07-01 03:01:04.574 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:04.586 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:04.597 return
2025-07-01 03:01:04.610 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:04.622 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:04.634 else:
2025-07-01 03:01:04.646 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:04.658 eqi = None
2025-07-01 03:01:04.666
2025-07-01 03:01:04.678 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:04.690 # identical
2025-07-01 03:01:04.698
2025-07-01 03:01:04.710 # pump out diffs from before the synch point
2025-07-01 03:01:04.722 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:04.734
2025-07-01 03:01:04.746 # do intraline marking on the synch pair
2025-07-01 03:01:04.754 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:04.766 if eqi is None:
2025-07-01 03:01:04.778 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:04.786 atags = btags = ""
2025-07-01 03:01:04.798 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:04.814 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:04.835 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:04.842 if tag == 'replace':
2025-07-01 03:01:04.858 atags += '^' * la
2025-07-01 03:01:04.870 btags += '^' * lb
2025-07-01 03:01:04.882 elif tag == 'delete':
2025-07-01 03:01:04.898 atags += '-' * la
2025-07-01 03:01:04.906 elif tag == 'insert':
2025-07-01 03:01:04.918 btags += '+' * lb
2025-07-01 03:01:04.934 elif tag == 'equal':
2025-07-01 03:01:04.942 atags += ' ' * la
2025-07-01 03:01:04.954 btags += ' ' * lb
2025-07-01 03:01:04.962 else:
2025-07-01 03:01:04.974 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:04.986 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:04.994 else:
2025-07-01 03:01:05.006 # the synch pair is identical
2025-07-01 03:01:05.016 yield '  ' + aelt
2025-07-01 03:01:05.037
2025-07-01 03:01:05.050 # pump out diffs from after the synch point
2025-07-01 03:01:05.062 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:05.074
2025-07-01 03:01:05.086 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:05.102 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:05.117
2025-07-01 03:01:05.130 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:05.146 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:05.158 alo = 130, ahi = 1101
2025-07-01 03:01:05.178 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:05.194 blo = 130, bhi = 1101
2025-07-01 03:01:05.202
2025-07-01 03:01:05.213 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:05.224 g = []
2025-07-01 03:01:05.234 if alo < ahi:
2025-07-01 03:01:05.240 if blo < bhi:
2025-07-01 03:01:05.245 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:05.250 else:
2025-07-01 03:01:05.265 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:05.274 elif blo < bhi:
2025-07-01 03:01:05.289 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:05.302
2025-07-01 03:01:05.314 >       yield from g
2025-07-01 03:01:05.326
2025-07-01 03:01:05.336 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:05.354 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:05.366
2025-07-01 03:01:05.382 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:05.398 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:05.406 alo = 130, ahi = 1101
2025-07-01 03:01:05.418 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:05.426 blo = 130, bhi = 1101
2025-07-01 03:01:05.438
2025-07-01 03:01:05.450 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:05.458 r"""
2025-07-01 03:01:05.470 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:05.482 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:05.498 synch point, and intraline difference marking is done on the
2025-07-01 03:01:05.506 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:05.522
2025-07-01 03:01:05.534 Example:
2025-07-01 03:01:05.554
2025-07-01 03:01:05.567 >>> d = Differ()
2025-07-01 03:01:05.575 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:05.592 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:05.602 >>> print(''.join(results), end="")
2025-07-01 03:01:05.610 - abcDefghiJkl
2025-07-01 03:01:05.630 + abcdefGhijkl
2025-07-01 03:01:05.655 """
2025-07-01 03:01:05.677
2025-07-01 03:01:05.689 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:05.702 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:05.716 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:05.734 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:05.742 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:05.758
2025-07-01 03:01:05.774 # search for the pair that matches best without being identical
2025-07-01 03:01:05.786 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:05.798 # on junk -- unless we have to)
2025-07-01 03:01:05.810 for j in range(blo, bhi):
2025-07-01 03:01:05.826 bj = b[j]
2025-07-01 03:01:05.842 cruncher.set_seq2(bj)
2025-07-01 03:01:05.854 for i in range(alo, ahi):
2025-07-01 03:01:05.866 ai = a[i]
2025-07-01 03:01:05.882 if ai == bj:
2025-07-01 03:01:05.894 if eqi is None:
2025-07-01 03:01:05.906 eqi, eqj = i, j
2025-07-01 03:01:05.924 continue
2025-07-01 03:01:05.942 cruncher.set_seq1(ai)
2025-07-01 03:01:05.950 # computing similarity is expensive, so use the quick
2025-07-01 03:01:05.962 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:05.978 # compares by a factor of 3.
2025-07-01 03:01:05.990 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:06.002 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:06.010 # of the computation is cached by cruncher
2025-07-01 03:01:06.026 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:06.039 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:06.050 cruncher.ratio() > best_ratio:
2025-07-01 03:01:06.066 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:06.082 if best_ratio < cutoff:
2025-07-01 03:01:06.094 # no non-identical "pretty close" pair
2025-07-01 03:01:06.106 if eqi is None:
2025-07-01 03:01:06.122 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:06.135 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:06.154 return
2025-07-01 03:01:06.166 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:06.174 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:06.186 else:
2025-07-01 03:01:06.202 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:06.220 eqi = None
2025-07-01 03:01:06.238
2025-07-01 03:01:06.246 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:06.258 # identical
2025-07-01 03:01:06.271
2025-07-01 03:01:06.278 # pump out diffs from before the synch point
2025-07-01 03:01:06.294 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:06.305
2025-07-01 03:01:06.311 # do intraline marking on the synch pair
2025-07-01 03:01:06.318 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:06.323 if eqi is None:
2025-07-01 03:01:06.328 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:06.332 atags = btags = ""
2025-07-01 03:01:06.338 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:06.342 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:06.348 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:06.354 if tag == 'replace':
2025-07-01 03:01:06.360 atags += '^' * la
2025-07-01 03:01:06.365 btags += '^' * lb
2025-07-01 03:01:06.371 elif tag == 'delete':
2025-07-01 03:01:06.375 atags += '-' * la
2025-07-01 03:01:06.380 elif tag == 'insert':
2025-07-01 03:01:06.385 btags += '+' * lb
2025-07-01 03:01:06.390 elif tag == 'equal':
2025-07-01 03:01:06.395 atags += ' ' * la
2025-07-01 03:01:06.400 btags += ' ' * lb
2025-07-01 03:01:06.404 else:
2025-07-01 03:01:06.409 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:06.414 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:06.418 else:
2025-07-01 03:01:06.422 # the synch pair is identical
2025-07-01 03:01:06.427 yield '  ' + aelt
2025-07-01 03:01:06.431
2025-07-01 03:01:06.436 # pump out diffs from after the synch point
2025-07-01 03:01:06.441 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:06.445
2025-07-01 03:01:06.449 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:06.454 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:06.458
2025-07-01 03:01:06.463 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:06.468 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:06.473 alo = 131, ahi = 1101
2025-07-01 03:01:06.480 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:06.484 blo = 131, bhi = 1101
2025-07-01 03:01:06.489
2025-07-01 03:01:06.493 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:06.497 g = []
2025-07-01 03:01:06.502 if alo < ahi:
2025-07-01 03:01:06.506 if blo < bhi:
2025-07-01 03:01:06.510 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:06.515 else:
2025-07-01 03:01:06.519 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:06.523 elif blo < bhi:
2025-07-01 03:01:06.528 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:06.532
2025-07-01 03:01:06.536 >       yield from g
2025-07-01 03:01:06.540
2025-07-01 03:01:06.545 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:06.549 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:06.554
2025-07-01 03:01:06.558 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:06.563 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:06.568 alo = 131, ahi = 1101
2025-07-01 03:01:06.574 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:06.578 blo = 131, bhi = 1101
2025-07-01 03:01:06.583
2025-07-01 03:01:06.587 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:06.593 r"""
2025-07-01 03:01:06.597 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:06.602 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:06.606 synch point, and intraline difference marking is done on the
2025-07-01 03:01:06.611 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:06.615
2025-07-01 03:01:06.620 Example:
2025-07-01 03:01:06.624
2025-07-01 03:01:06.628 >>> d = Differ()
2025-07-01 03:01:06.635 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:06.639 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:06.645 >>> print(''.join(results), end="")
2025-07-01 03:01:06.651 - abcDefghiJkl
2025-07-01 03:01:06.661 + abcdefGhijkl
2025-07-01 03:01:06.671 """
2025-07-01 03:01:06.678
2025-07-01 03:01:06.697 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:06.709 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:06.718 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:06.733 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:06.741 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:06.762
2025-07-01 03:01:06.769 # search for the pair that matches best without being identical
2025-07-01 03:01:06.782 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:06.797 # on junk -- unless we have to)
2025-07-01 03:01:06.814 for j in range(blo, bhi):
2025-07-01 03:01:06.830 bj = b[j]
2025-07-01 03:01:06.838 cruncher.set_seq2(bj)
2025-07-01 03:01:06.845 for i in range(alo, ahi):
2025-07-01 03:01:06.858 ai = a[i]
2025-07-01 03:01:06.866 if ai == bj:
2025-07-01 03:01:06.876 if eqi is None:
2025-07-01 03:01:06.890 eqi, eqj = i, j
2025-07-01 03:01:06.907 continue
2025-07-01 03:01:06.922 cruncher.set_seq1(ai)
2025-07-01 03:01:06.927 # computing similarity is expensive, so use the quick
2025-07-01 03:01:06.946 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:06.954 # compares by a factor of 3.
2025-07-01 03:01:06.961 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:06.978 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:06.990 # of the computation is cached by cruncher
2025-07-01 03:01:07.001 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:07.015 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:07.026 cruncher.ratio() > best_ratio:
2025-07-01 03:01:07.038 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:07.056 if best_ratio < cutoff:
2025-07-01 03:01:07.070 # no non-identical "pretty close" pair
2025-07-01 03:01:07.082 if eqi is None:
2025-07-01 03:01:07.093 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:07.106 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:07.118 return
2025-07-01 03:01:07.130 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:07.138 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:07.149 else:
2025-07-01 03:01:07.164 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:07.178 eqi = None
2025-07-01 03:01:07.190
2025-07-01 03:01:07.202 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:07.214 # identical
2025-07-01 03:01:07.222
2025-07-01 03:01:07.234 # pump out diffs from before the synch point
2025-07-01 03:01:07.250 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:07.262
2025-07-01 03:01:07.270 # do intraline marking on the synch pair
2025-07-01 03:01:07.282 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:07.290 if eqi is None:
2025-07-01 03:01:07.298 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:07.312 atags = btags = ""
2025-07-01 03:01:07.330 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:07.342 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:07.350 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:07.362 if tag == 'replace':
2025-07-01 03:01:07.370 atags += '^' * la
2025-07-01 03:01:07.382 btags += '^' * lb
2025-07-01 03:01:07.394 elif tag == 'delete':
2025-07-01 03:01:07.410 atags += '-' * la
2025-07-01 03:01:07.421 elif tag == 'insert':
2025-07-01 03:01:07.434 btags += '+' * lb
2025-07-01 03:01:07.449 elif tag == 'equal':
2025-07-01 03:01:07.462 atags += ' ' * la
2025-07-01 03:01:07.468 btags += ' ' * lb
2025-07-01 03:01:07.472 else:
2025-07-01 03:01:07.477 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:07.482 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:07.487 else:
2025-07-01 03:01:07.491 # the synch pair is identical
2025-07-01 03:01:07.497 yield '  ' + aelt
2025-07-01 03:01:07.502
2025-07-01 03:01:07.507 # pump out diffs from after the synch point
2025-07-01 03:01:07.511 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:07.516
2025-07-01 03:01:07.521 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:07.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:07.530
2025-07-01 03:01:07.535 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:07.541 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:07.549 alo = 132, ahi = 1101
2025-07-01 03:01:07.558 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:07.565 blo = 132, bhi = 1101
2025-07-01 03:01:07.570
2025-07-01 03:01:07.574 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:07.579 g = []
2025-07-01 03:01:07.583 if alo < ahi:
2025-07-01 03:01:07.588 if blo < bhi:
2025-07-01 03:01:07.592 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:07.597 else:
2025-07-01 03:01:07.602 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:07.607 elif blo < bhi:
2025-07-01 03:01:07.611 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:07.616
2025-07-01 03:01:07.620 >       yield from g
2025-07-01 03:01:07.626
2025-07-01 03:01:07.631 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:07.636 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:07.641
2025-07-01 03:01:07.646 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:07.652 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:07.657 alo = 132, ahi = 1101
2025-07-01 03:01:07.664 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:07.669 blo = 132, bhi = 1101
2025-07-01 03:01:07.674
2025-07-01 03:01:07.680 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:07.686 r"""
2025-07-01 03:01:07.693 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:07.700 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:07.707 synch point, and intraline difference marking is done on the
2025-07-01 03:01:07.722 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:07.728
2025-07-01 03:01:07.733 Example:
2025-07-01 03:01:07.738
2025-07-01 03:01:07.743 >>> d = Differ()
2025-07-01 03:01:07.748 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:07.752 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:07.757 >>> print(''.join(results), end="")
2025-07-01 03:01:07.762 - abcDefghiJkl
2025-07-01 03:01:07.771 + abcdefGhijkl
2025-07-01 03:01:07.780 """
2025-07-01 03:01:07.785
2025-07-01 03:01:07.789 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:07.794 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:07.798 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:07.803 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:07.808 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:07.812
2025-07-01 03:01:07.817 # search for the pair that matches best without being identical
2025-07-01 03:01:07.821 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:07.826 # on junk -- unless we have to)
2025-07-01 03:01:07.831 for j in range(blo, bhi):
2025-07-01 03:01:07.835 bj = b[j]
2025-07-01 03:01:07.840 cruncher.set_seq2(bj)
2025-07-01 03:01:07.844 for i in range(alo, ahi):
2025-07-01 03:01:07.849 ai = a[i]
2025-07-01 03:01:07.853 if ai == bj:
2025-07-01 03:01:07.857 if eqi is None:
2025-07-01 03:01:07.862 eqi, eqj = i, j
2025-07-01 03:01:07.866 continue
2025-07-01 03:01:07.870 cruncher.set_seq1(ai)
2025-07-01 03:01:07.875 # computing similarity is expensive, so use the quick
2025-07-01 03:01:07.880 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:07.884 # compares by a factor of 3.
2025-07-01 03:01:07.888 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:07.893 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:07.897 # of the computation is cached by cruncher
2025-07-01 03:01:07.902 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:07.906 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:07.912 cruncher.ratio() > best_ratio:
2025-07-01 03:01:07.917 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:07.921 if best_ratio < cutoff:
2025-07-01 03:01:07.926 # no non-identical "pretty close" pair
2025-07-01 03:01:07.930 if eqi is None:
2025-07-01 03:01:07.935 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:07.939 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:07.944 return
2025-07-01 03:01:07.949 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:07.954 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:07.958 else:
2025-07-01 03:01:07.962 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:07.967 eqi = None
2025-07-01 03:01:07.971
2025-07-01 03:01:07.976 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:07.980 # identical
2025-07-01 03:01:07.984
2025-07-01 03:01:07.989 # pump out diffs from before the synch point
2025-07-01 03:01:07.993 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:07.998
2025-07-01 03:01:08.002 # do intraline marking on the synch pair
2025-07-01 03:01:08.007 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:08.012 if eqi is None:
2025-07-01 03:01:08.017 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:08.022 atags = btags = ""
2025-07-01 03:01:08.026 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:08.031 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:08.036 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:08.040 if tag == 'replace':
2025-07-01 03:01:08.045 atags += '^' * la
2025-07-01 03:01:08.049 btags += '^' * lb
2025-07-01 03:01:08.054 elif tag == 'delete':
2025-07-01 03:01:08.058 atags += '-' * la
2025-07-01 03:01:08.062 elif tag == 'insert':
2025-07-01 03:01:08.067 btags += '+' * lb
2025-07-01 03:01:08.071 elif tag == 'equal':
2025-07-01 03:01:08.076 atags += ' ' * la
2025-07-01 03:01:08.080 btags += ' ' * lb
2025-07-01 03:01:08.085 else:
2025-07-01 03:01:08.089 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:08.094 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:08.098 else:
2025-07-01 03:01:08.103 # the synch pair is identical
2025-07-01 03:01:08.107 yield '  ' + aelt
2025-07-01 03:01:08.111
2025-07-01 03:01:08.116 # pump out diffs from after the synch point
2025-07-01 03:01:08.120 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:08.125
2025-07-01 03:01:08.129 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:08.134 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:08.138
2025-07-01 03:01:08.142 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:08.147 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:08.151 alo = 133, ahi = 1101
2025-07-01 03:01:08.156 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:08.160 blo = 133, bhi = 1101
2025-07-01 03:01:08.165
2025-07-01 03:01:08.171 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:08.176 g = []
2025-07-01 03:01:08.180 if alo < ahi:
2025-07-01 03:01:08.185 if blo < bhi:
2025-07-01 03:01:08.190 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:08.194 else:
2025-07-01 03:01:08.198 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:08.203 elif blo < bhi:
2025-07-01 03:01:08.207 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:08.214
2025-07-01 03:01:08.218 >       yield from g
2025-07-01 03:01:08.223
2025-07-01 03:01:08.227 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:08.232 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:08.236
2025-07-01 03:01:08.241 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:08.245 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:08.250 alo = 133, ahi = 1101
2025-07-01 03:01:08.254 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:08.259 blo = 133, bhi = 1101
2025-07-01 03:01:08.263
2025-07-01 03:01:08.268 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:08.274 r"""
2025-07-01 03:01:08.278 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:08.283 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:08.288 synch point, and intraline difference marking is done on the
2025-07-01 03:01:08.293 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:08.297
2025-07-01 03:01:08.301 Example:
2025-07-01 03:01:08.306
2025-07-01 03:01:08.310 >>> d = Differ()
2025-07-01 03:01:08.315 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:08.321 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:08.325 >>> print(''.join(results), end="")
2025-07-01 03:01:08.330 - abcDefghiJkl
2025-07-01 03:01:08.339 + abcdefGhijkl
2025-07-01 03:01:08.348 """
2025-07-01 03:01:08.352
2025-07-01 03:01:08.356 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:08.361 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:08.365 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:08.369 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:08.374 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:08.379
2025-07-01 03:01:08.384 # search for the pair that matches best without being identical
2025-07-01 03:01:08.389 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:08.393 # on junk -- unless we have to)
2025-07-01 03:01:08.398 for j in range(blo, bhi):
2025-07-01 03:01:08.402 bj = b[j]
2025-07-01 03:01:08.406 cruncher.set_seq2(bj)
2025-07-01 03:01:08.411 for i in range(alo, ahi):
2025-07-01 03:01:08.415 ai = a[i]
2025-07-01 03:01:08.420 if ai == bj:
2025-07-01 03:01:08.424 if eqi is None:
2025-07-01 03:01:08.429 eqi, eqj = i, j
2025-07-01 03:01:08.434 continue
2025-07-01 03:01:08.439 cruncher.set_seq1(ai)
2025-07-01 03:01:08.443 # computing similarity is expensive, so use the quick
2025-07-01 03:01:08.448 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:08.453 # compares by a factor of 3.
2025-07-01 03:01:08.458 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:08.469 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:08.482 # of the computation is cached by cruncher
2025-07-01 03:01:08.497 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:08.510 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:08.522 cruncher.ratio() > best_ratio:
2025-07-01 03:01:08.534 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:08.546 if best_ratio < cutoff:
2025-07-01 03:01:08.552 # no non-identical "pretty close" pair
2025-07-01 03:01:08.566 if eqi is None:
2025-07-01 03:01:08.578 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:08.590 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:08.606 return
2025-07-01 03:01:08.618 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:08.626 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:08.642 else:
2025-07-01 03:01:08.659 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:08.670 eqi = None
2025-07-01 03:01:08.682
2025-07-01 03:01:08.694 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:08.710 # identical
2025-07-01 03:01:08.718
2025-07-01 03:01:08.728 # pump out diffs from before the synch point
2025-07-01 03:01:08.741 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:08.754
2025-07-01 03:01:08.766 # do intraline marking on the synch pair
2025-07-01 03:01:08.774 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:08.786 if eqi is None:
2025-07-01 03:01:08.798 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:08.806 atags = btags = ""
2025-07-01 03:01:08.818 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:08.826 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:08.842 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:08.852 if tag == 'replace':
2025-07-01 03:01:08.866 atags += '^' * la
2025-07-01 03:01:08.873 btags += '^' * lb
2025-07-01 03:01:08.890 elif tag == 'delete':
2025-07-01 03:01:08.898 atags += '-' * la
2025-07-01 03:01:08.910 elif tag == 'insert':
2025-07-01 03:01:08.918 btags += '+' * lb
2025-07-01 03:01:08.930 elif tag == 'equal':
2025-07-01 03:01:08.944 atags += ' ' * la
2025-07-01 03:01:08.950 btags += ' ' * lb
2025-07-01 03:01:08.966 else:
2025-07-01 03:01:08.978 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:08.986 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:08.998 else:
2025-07-01 03:01:09.014 # the synch pair is identical
2025-07-01 03:01:09.022 yield '  ' + aelt
2025-07-01 03:01:09.030
2025-07-01 03:01:09.044 # pump out diffs from after the synch point
2025-07-01 03:01:09.058 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:09.065
2025-07-01 03:01:09.077 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:09.089 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:09.101
2025-07-01 03:01:09.116 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:09.125 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:09.138 alo = 136, ahi = 1101
2025-07-01 03:01:09.146 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:09.157 blo = 136, bhi = 1101
2025-07-01 03:01:09.169
2025-07-01 03:01:09.178 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:09.191 g = []
2025-07-01 03:01:09.209 if alo < ahi:
2025-07-01 03:01:09.222 if blo < bhi:
2025-07-01 03:01:09.230 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:09.243 else:
2025-07-01 03:01:09.261 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:09.276 elif blo < bhi:
2025-07-01 03:01:09.286 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:09.298
2025-07-01 03:01:09.308 >       yield from g
2025-07-01 03:01:09.320
2025-07-01 03:01:09.334 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:09.350 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:09.362
2025-07-01 03:01:09.370 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:09.381 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:09.391 alo = 136, ahi = 1101
2025-07-01 03:01:09.402 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:09.410 blo = 136, bhi = 1101
2025-07-01 03:01:09.426
2025-07-01 03:01:09.438 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:09.452 r"""
2025-07-01 03:01:09.461 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:09.473 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:09.486 synch point, and intraline difference marking is done on the
2025-07-01 03:01:09.497 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:09.506
2025-07-01 03:01:09.518 Example:
2025-07-01 03:01:09.530
2025-07-01 03:01:09.541 >>> d = Differ()
2025-07-01 03:01:09.553 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:09.563 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:09.574 >>> print(''.join(results), end="")
2025-07-01 03:01:09.579 - abcDefghiJkl
2025-07-01 03:01:09.598 + abcdefGhijkl
2025-07-01 03:01:09.616 """
2025-07-01 03:01:09.620
2025-07-01 03:01:09.626 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:09.633 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:09.649 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:09.659 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:09.666 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:09.674
2025-07-01 03:01:09.682 # search for the pair that matches best without being identical
2025-07-01 03:01:09.697 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:09.709 # on junk -- unless we have to)
2025-07-01 03:01:09.721 for j in range(blo, bhi):
2025-07-01 03:01:09.732 bj = b[j]
2025-07-01 03:01:09.741 cruncher.set_seq2(bj)
2025-07-01 03:01:09.753 for i in range(alo, ahi):
2025-07-01 03:01:09.765 ai = a[i]
2025-07-01 03:01:09.774 if ai == bj:
2025-07-01 03:01:09.787 if eqi is None:
2025-07-01 03:01:09.802 eqi, eqj = i, j
2025-07-01 03:01:09.807 continue
2025-07-01 03:01:09.812 cruncher.set_seq1(ai)
2025-07-01 03:01:09.822 # computing similarity is expensive, so use the quick
2025-07-01 03:01:09.834 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:09.842 # compares by a factor of 3.
2025-07-01 03:01:09.854 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:09.862 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:09.874 # of the computation is cached by cruncher
2025-07-01 03:01:09.882 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:09.894 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:09.906 cruncher.ratio() > best_ratio:
2025-07-01 03:01:09.914 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:09.930 if best_ratio < cutoff:
2025-07-01 03:01:09.940 # no non-identical "pretty close" pair
2025-07-01 03:01:09.954 if eqi is None:
2025-07-01 03:01:09.962 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:09.978 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:09.987 return
2025-07-01 03:01:09.998 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:10.006 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:10.014 else:
2025-07-01 03:01:10.027 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:10.042 eqi = None
2025-07-01 03:01:10.050
2025-07-01 03:01:10.061 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:10.070 # identical
2025-07-01 03:01:10.082
2025-07-01 03:01:10.090 # pump out diffs from before the synch point
2025-07-01 03:01:10.101 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:10.109
2025-07-01 03:01:10.123 # do intraline marking on the synch pair
2025-07-01 03:01:10.132 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:10.158 if eqi is None:
2025-07-01 03:01:10.170 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:10.178 atags = btags = ""
2025-07-01 03:01:10.190 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:10.198 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:10.210 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:10.217 if tag == 'replace':
2025-07-01 03:01:10.230 atags += '^' * la
2025-07-01 03:01:10.238 btags += '^' * lb
2025-07-01 03:01:10.250 elif tag == 'delete':
2025-07-01 03:01:10.262 atags += '-' * la
2025-07-01 03:01:10.270 elif tag == 'insert':
2025-07-01 03:01:10.282 btags += '+' * lb
2025-07-01 03:01:10.290 elif tag == 'equal':
2025-07-01 03:01:10.302 atags += ' ' * la
2025-07-01 03:01:10.310 btags += ' ' * lb
2025-07-01 03:01:10.318 else:
2025-07-01 03:01:10.330 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:10.338 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:10.351 else:
2025-07-01 03:01:10.359 # the synch pair is identical
2025-07-01 03:01:10.366 yield '  ' + aelt
2025-07-01 03:01:10.377
2025-07-01 03:01:10.388 # pump out diffs from after the synch point
2025-07-01 03:01:10.398 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:10.406
2025-07-01 03:01:10.416 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:10.429 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:10.438
2025-07-01 03:01:10.450 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:10.462 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:10.470 alo = 137, ahi = 1101
2025-07-01 03:01:10.478 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:10.490 blo = 137, bhi = 1101
2025-07-01 03:01:10.502
2025-07-01 03:01:10.514 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:10.526 g = []
2025-07-01 03:01:10.534 if alo < ahi:
2025-07-01 03:01:10.546 if blo < bhi:
2025-07-01 03:01:10.554 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:10.566 else:
2025-07-01 03:01:10.591 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:10.602 elif blo < bhi:
2025-07-01 03:01:10.618 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:10.628
2025-07-01 03:01:10.638 >       yield from g
2025-07-01 03:01:10.654
2025-07-01 03:01:10.666 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:10.677 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:10.693
2025-07-01 03:01:10.706 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:10.722 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:10.738 alo = 137, ahi = 1101
2025-07-01 03:01:10.750 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:10.766 blo = 137, bhi = 1101
2025-07-01 03:01:10.778
2025-07-01 03:01:10.794 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:10.806 r"""
2025-07-01 03:01:10.822 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:10.840 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:10.857 synch point, and intraline difference marking is done on the
2025-07-01 03:01:10.865 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:10.878
2025-07-01 03:01:10.885 Example:
2025-07-01 03:01:10.898
2025-07-01 03:01:10.906 >>> d = Differ()
2025-07-01 03:01:10.918 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:10.926 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:10.938 >>> print(''.join(results), end="")
2025-07-01 03:01:10.950 - abcDefghiJkl
2025-07-01 03:01:10.970 + abcdefGhijkl
2025-07-01 03:01:10.990 """
2025-07-01 03:01:11.001
2025-07-01 03:01:11.014 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:11.019 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:11.024 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:11.028 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:11.033 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:11.037
2025-07-01 03:01:11.044 # search for the pair that matches best without being identical
2025-07-01 03:01:11.049 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:11.054 # on junk -- unless we have to)
2025-07-01 03:01:11.058 for j in range(blo, bhi):
2025-07-01 03:01:11.062 bj = b[j]
2025-07-01 03:01:11.067 cruncher.set_seq2(bj)
2025-07-01 03:01:11.072 for i in range(alo, ahi):
2025-07-01 03:01:11.076 ai = a[i]
2025-07-01 03:01:11.081 if ai == bj:
2025-07-01 03:01:11.085 if eqi is None:
2025-07-01 03:01:11.090 eqi, eqj = i, j
2025-07-01 03:01:11.094 continue
2025-07-01 03:01:11.099 cruncher.set_seq1(ai)
2025-07-01 03:01:11.104 # computing similarity is expensive, so use the quick
2025-07-01 03:01:11.109 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:11.113 # compares by a factor of 3.
2025-07-01 03:01:11.118 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:11.122 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:11.127 # of the computation is cached by cruncher
2025-07-01 03:01:11.131 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:11.136 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:11.140 cruncher.ratio() > best_ratio:
2025-07-01 03:01:11.145 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:11.149 if best_ratio < cutoff:
2025-07-01 03:01:11.154 # no non-identical "pretty close" pair
2025-07-01 03:01:11.159 if eqi is None:
2025-07-01 03:01:11.163 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:11.168 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:11.172 return
2025-07-01 03:01:11.177 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:11.181 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:11.185 else:
2025-07-01 03:01:11.190 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:11.194 eqi = None
2025-07-01 03:01:11.199
2025-07-01 03:01:11.203 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:11.209 # identical
2025-07-01 03:01:11.213
2025-07-01 03:01:11.219 # pump out diffs from before the synch point
2025-07-01 03:01:11.224 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:11.229
2025-07-01 03:01:11.233 # do intraline marking on the synch pair
2025-07-01 03:01:11.238 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:11.242 if eqi is None:
2025-07-01 03:01:11.247 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:11.252 atags = btags = ""
2025-07-01 03:01:11.256 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:11.261 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:11.265 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:11.270 if tag == 'replace':
2025-07-01 03:01:11.274 atags += '^' * la
2025-07-01 03:01:11.278 btags += '^' * lb
2025-07-01 03:01:11.283 elif tag == 'delete':
2025-07-01 03:01:11.287 atags += '-' * la
2025-07-01 03:01:11.292 elif tag == 'insert':
2025-07-01 03:01:11.296 btags += '+' * lb
2025-07-01 03:01:11.300 elif tag == 'equal':
2025-07-01 03:01:11.305 atags += ' ' * la
2025-07-01 03:01:11.309 btags += ' ' * lb
2025-07-01 03:01:11.314 else:
2025-07-01 03:01:11.318 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:11.323 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:11.327 else:
2025-07-01 03:01:11.331 # the synch pair is identical
2025-07-01 03:01:11.336 yield '  ' + aelt
2025-07-01 03:01:11.341
2025-07-01 03:01:11.345 # pump out diffs from after the synch point
2025-07-01 03:01:11.351 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:11.356
2025-07-01 03:01:11.362 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:11.367 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:11.372
2025-07-01 03:01:11.377 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:11.383 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:11.388 alo = 138, ahi = 1101
2025-07-01 03:01:11.395 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:11.401 blo = 138, bhi = 1101
2025-07-01 03:01:11.406
2025-07-01 03:01:11.412 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:11.417 g = []
2025-07-01 03:01:11.421 if alo < ahi:
2025-07-01 03:01:11.425 if blo < bhi:
2025-07-01 03:01:11.430 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:11.434 else:
2025-07-01 03:01:11.440 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:11.445 elif blo < bhi:
2025-07-01 03:01:11.450 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:11.454
2025-07-01 03:01:11.460 >       yield from g
2025-07-01 03:01:11.465
2025-07-01 03:01:11.470 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:11.475 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:11.479
2025-07-01 03:01:11.484 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:11.489 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:11.494 alo = 138, ahi = 1101
2025-07-01 03:01:11.501 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:11.506 blo = 138, bhi = 1101
2025-07-01 03:01:11.510
2025-07-01 03:01:11.516 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:11.523 r"""
2025-07-01 03:01:11.528 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:11.532 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:11.537 synch point, and intraline difference marking is done on the
2025-07-01 03:01:11.541 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:11.546
2025-07-01 03:01:11.553 Example:
2025-07-01 03:01:11.557
2025-07-01 03:01:11.562 >>> d = Differ()
2025-07-01 03:01:11.566 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:11.571 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:11.576 >>> print(''.join(results), end="")
2025-07-01 03:01:11.580 - abcDefghiJkl
2025-07-01 03:01:11.589 + abcdefGhijkl
2025-07-01 03:01:11.606 """
2025-07-01 03:01:11.614
2025-07-01 03:01:11.621 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:11.630 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:11.639 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:11.646 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:11.654 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:11.665
2025-07-01 03:01:11.677 # search for the pair that matches best without being identical
2025-07-01 03:01:11.687 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:11.697 # on junk -- unless we have to)
2025-07-01 03:01:11.710 for j in range(blo, bhi):
2025-07-01 03:01:11.716 bj = b[j]
2025-07-01 03:01:11.726 cruncher.set_seq2(bj)
2025-07-01 03:01:11.732 for i in range(alo, ahi):
2025-07-01 03:01:11.741 ai = a[i]
2025-07-01 03:01:11.752 if ai == bj:
2025-07-01 03:01:11.762 if eqi is None:
2025-07-01 03:01:11.770 eqi, eqj = i, j
2025-07-01 03:01:11.783 continue
2025-07-01 03:01:11.798 cruncher.set_seq1(ai)
2025-07-01 03:01:11.806 # computing similarity is expensive, so use the quick
2025-07-01 03:01:11.814 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:11.822 # compares by a factor of 3.
2025-07-01 03:01:11.838 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:11.846 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:11.862 # of the computation is cached by cruncher
2025-07-01 03:01:11.874 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:11.890 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:11.901 cruncher.ratio() > best_ratio:
2025-07-01 03:01:11.913 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:11.930 if best_ratio < cutoff:
2025-07-01 03:01:11.946 # no non-identical "pretty close" pair
2025-07-01 03:01:11.959 if eqi is None:
2025-07-01 03:01:11.977 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:11.989 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:12.001 return
2025-07-01 03:01:12.017 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:12.025 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:12.031 else:
2025-07-01 03:01:12.045 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:12.058 eqi = None
2025-07-01 03:01:12.070
2025-07-01 03:01:12.082 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:12.094 # identical
2025-07-01 03:01:12.106
2025-07-01 03:01:12.118 # pump out diffs from before the synch point
2025-07-01 03:01:12.126 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:12.138
2025-07-01 03:01:12.150 # do intraline marking on the synch pair
2025-07-01 03:01:12.158 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:12.174 if eqi is None:
2025-07-01 03:01:12.182 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:12.198 atags = btags = ""
2025-07-01 03:01:12.206 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:12.222 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:12.230 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:12.246 if tag == 'replace':
2025-07-01 03:01:12.254 atags += '^' * la
2025-07-01 03:01:12.270 btags += '^' * lb
2025-07-01 03:01:12.278 elif tag == 'delete':
2025-07-01 03:01:12.294 atags += '-' * la
2025-07-01 03:01:12.302 elif tag == 'insert':
2025-07-01 03:01:12.318 btags += '+' * lb
2025-07-01 03:01:12.326 elif tag == 'equal':
2025-07-01 03:01:12.338 atags += ' ' * la
2025-07-01 03:01:12.346 btags += ' ' * lb
2025-07-01 03:01:12.362 else:
2025-07-01 03:01:12.370 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:12.386 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:12.394 else:
2025-07-01 03:01:12.410 # the synch pair is identical
2025-07-01 03:01:12.418 yield '  ' + aelt
2025-07-01 03:01:12.434
2025-07-01 03:01:12.446 # pump out diffs from after the synch point
2025-07-01 03:01:12.458 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:12.470
2025-07-01 03:01:12.482 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:12.494 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:12.510
2025-07-01 03:01:12.518 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:12.534 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:12.546 alo = 139, ahi = 1101
2025-07-01 03:01:12.562 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:12.570 blo = 139, bhi = 1101
2025-07-01 03:01:12.582
2025-07-01 03:01:12.594 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:12.602 g = []
2025-07-01 03:01:12.613 if alo < ahi:
2025-07-01 03:01:12.630 if blo < bhi:
2025-07-01 03:01:12.638 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:12.650 else:
2025-07-01 03:01:12.666 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:12.678 elif blo < bhi:
2025-07-01 03:01:12.686 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:12.702
2025-07-01 03:01:12.710 >       yield from g
2025-07-01 03:01:12.718
2025-07-01 03:01:12.734 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:12.744 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:12.758
2025-07-01 03:01:12.766 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:12.778 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:12.790 alo = 139, ahi = 1101
2025-07-01 03:01:12.804 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:12.822 blo = 139, bhi = 1101
2025-07-01 03:01:12.830
2025-07-01 03:01:12.842 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:12.850 r"""
2025-07-01 03:01:12.864 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:12.874 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:12.886 synch point, and intraline difference marking is done on the
2025-07-01 03:01:12.902 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:12.914
2025-07-01 03:01:12.926 Example:
2025-07-01 03:01:12.934
2025-07-01 03:01:12.946 >>> d = Differ()
2025-07-01 03:01:12.957 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:12.966 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:12.980 >>> print(''.join(results), end="")
2025-07-01 03:01:12.994 - abcDefghiJkl
2025-07-01 03:01:13.015 + abcdefGhijkl
2025-07-01 03:01:13.045 """
2025-07-01 03:01:13.057
2025-07-01 03:01:13.070 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:13.081 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:13.092 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:13.098 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:13.110 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:13.126
2025-07-01 03:01:13.138 # search for the pair that matches best without being identical
2025-07-01 03:01:13.146 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:13.158 # on junk -- unless we have to)
2025-07-01 03:01:13.169 for j in range(blo, bhi):
2025-07-01 03:01:13.182 bj = b[j]
2025-07-01 03:01:13.190 cruncher.set_seq2(bj)
2025-07-01 03:01:13.206 for i in range(alo, ahi):
2025-07-01 03:01:13.218 ai = a[i]
2025-07-01 03:01:13.229 if ai == bj:
2025-07-01 03:01:13.242 if eqi is None:
2025-07-01 03:01:13.250 eqi, eqj = i, j
2025-07-01 03:01:13.266 continue
2025-07-01 03:01:13.278 cruncher.set_seq1(ai)
2025-07-01 03:01:13.286 # computing similarity is expensive, so use the quick
2025-07-01 03:01:13.302 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:13.310 # compares by a factor of 3.
2025-07-01 03:01:13.322 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:13.334 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:13.342 # of the computation is cached by cruncher
2025-07-01 03:01:13.354 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:13.365 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:13.378 cruncher.ratio() > best_ratio:
2025-07-01 03:01:13.390 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:13.406 if best_ratio < cutoff:
2025-07-01 03:01:13.418 # no non-identical "pretty close" pair
2025-07-01 03:01:13.434 if eqi is None:
2025-07-01 03:01:13.444 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:13.462 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:13.470 return
2025-07-01 03:01:13.483 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:13.504 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:13.518 else:
2025-07-01 03:01:13.533 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:13.542 eqi = None
2025-07-01 03:01:13.554
2025-07-01 03:01:13.562 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:13.574 # identical
2025-07-01 03:01:13.586
2025-07-01 03:01:13.594 # pump out diffs from before the synch point
2025-07-01 03:01:13.606 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:13.614
2025-07-01 03:01:13.630 # do intraline marking on the synch pair
2025-07-01 03:01:13.638 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:13.650 if eqi is None:
2025-07-01 03:01:13.661 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:13.670 atags = btags = ""
2025-07-01 03:01:13.679 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:13.690 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:13.706 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:13.716 if tag == 'replace':
2025-07-01 03:01:13.730 atags += '^' * la
2025-07-01 03:01:13.738 btags += '^' * lb
2025-07-01 03:01:13.748 elif tag == 'delete':
2025-07-01 03:01:13.757 atags += '-' * la
2025-07-01 03:01:13.770 elif tag == 'insert':
2025-07-01 03:01:13.778 btags += '+' * lb
2025-07-01 03:01:13.790 elif tag == 'equal':
2025-07-01 03:01:13.799 atags += ' ' * la
2025-07-01 03:01:13.814 btags += ' ' * lb
2025-07-01 03:01:13.822 else:
2025-07-01 03:01:13.833 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:13.842 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:13.858 else:
2025-07-01 03:01:13.870 # the synch pair is identical
2025-07-01 03:01:13.878 yield '  ' + aelt
2025-07-01 03:01:13.890
2025-07-01 03:01:13.906 # pump out diffs from after the synch point
2025-07-01 03:01:13.914 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:13.926
2025-07-01 03:01:13.938 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:13.945 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:13.952
2025-07-01 03:01:13.966 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:13.978 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:13.987 alo = 140, ahi = 1101
2025-07-01 03:01:14.005 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:14.015 blo = 140, bhi = 1101
2025-07-01 03:01:14.030
2025-07-01 03:01:14.037 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:14.044 g = []
2025-07-01 03:01:14.062 if alo < ahi:
2025-07-01 03:01:14.074 if blo < bhi:
2025-07-01 03:01:14.084 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:14.098 else:
2025-07-01 03:01:14.106 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:14.122 elif blo < bhi:
2025-07-01 03:01:14.130 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:14.142
2025-07-01 03:01:14.158 >       yield from g
2025-07-01 03:01:14.166
2025-07-01 03:01:14.174 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:14.186 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:14.198
2025-07-01 03:01:14.206 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:14.218 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:14.230 alo = 140, ahi = 1101
2025-07-01 03:01:14.246 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:14.254 blo = 140, bhi = 1101
2025-07-01 03:01:14.265
2025-07-01 03:01:14.277 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:14.290 r"""
2025-07-01 03:01:14.298 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:14.314 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:14.330 synch point, and intraline difference marking is done on the
2025-07-01 03:01:14.339 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:14.350
2025-07-01 03:01:14.362 Example:
2025-07-01 03:01:14.371
2025-07-01 03:01:14.390 >>> d = Differ()
2025-07-01 03:01:14.404 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:14.425 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:14.442 >>> print(''.join(results), end="")
2025-07-01 03:01:14.456 - abcDefghiJkl
2025-07-01 03:01:14.478 + abcdefGhijkl
2025-07-01 03:01:14.494 """
2025-07-01 03:01:14.506
2025-07-01 03:01:14.513 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:14.525 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:14.534 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:14.546 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:14.554 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:14.566
2025-07-01 03:01:14.574 # search for the pair that matches best without being identical
2025-07-01 03:01:14.586 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:14.598 # on junk -- unless we have to)
2025-07-01 03:01:14.610 for j in range(blo, bhi):
2025-07-01 03:01:14.622 bj = b[j]
2025-07-01 03:01:14.630 cruncher.set_seq2(bj)
2025-07-01 03:01:14.644 for i in range(alo, ahi):
2025-07-01 03:01:14.654 ai = a[i]
2025-07-01 03:01:14.666 if ai == bj:
2025-07-01 03:01:14.678 if eqi is None:
2025-07-01 03:01:14.690 eqi, eqj = i, j
2025-07-01 03:01:14.698 continue
2025-07-01 03:01:14.706 cruncher.set_seq1(ai)
2025-07-01 03:01:14.722 # computing similarity is expensive, so use the quick
2025-07-01 03:01:14.734 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:14.742 # compares by a factor of 3.
2025-07-01 03:01:14.758 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:14.766 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:14.778 # of the computation is cached by cruncher
2025-07-01 03:01:14.786 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:14.796 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:14.810 cruncher.ratio() > best_ratio:
2025-07-01 03:01:14.826 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:14.834 if best_ratio < cutoff:
2025-07-01 03:01:14.846 # no non-identical "pretty close" pair
2025-07-01 03:01:14.862 if eqi is None:
2025-07-01 03:01:14.874 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:14.882 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:14.894 return
2025-07-01 03:01:14.902 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:14.914 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:14.922 else:
2025-07-01 03:01:14.938 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:14.945 eqi = None
2025-07-01 03:01:14.958
2025-07-01 03:01:14.965 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:14.973 # identical
2025-07-01 03:01:14.990
2025-07-01 03:01:14.998 # pump out diffs from before the synch point
2025-07-01 03:01:15.014 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:15.026
2025-07-01 03:01:15.038 # do intraline marking on the synch pair
2025-07-01 03:01:15.050 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:15.062 if eqi is None:
2025-07-01 03:01:15.074 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:15.086 atags = btags = ""
2025-07-01 03:01:15.094 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:15.110 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:15.118 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:15.126 if tag == 'replace':
2025-07-01 03:01:15.138 atags += '^' * la
2025-07-01 03:01:15.146 btags += '^' * lb
2025-07-01 03:01:15.160 elif tag == 'delete':
2025-07-01 03:01:15.174 atags += '-' * la
2025-07-01 03:01:15.182 elif tag == 'insert':
2025-07-01 03:01:15.194 btags += '+' * lb
2025-07-01 03:01:15.202 elif tag == 'equal':
2025-07-01 03:01:15.218 atags += ' ' * la
2025-07-01 03:01:15.230 btags += ' ' * lb
2025-07-01 03:01:15.238 else:
2025-07-01 03:01:15.249 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:15.261 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:15.271 else:
2025-07-01 03:01:15.282 # the synch pair is identical
2025-07-01 03:01:15.294 yield '  ' + aelt
2025-07-01 03:01:15.301
2025-07-01 03:01:15.318 # pump out diffs from after the synch point
2025-07-01 03:01:15.326 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:15.338
2025-07-01 03:01:15.350 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:15.358 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:15.370
2025-07-01 03:01:15.382 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:15.390 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:15.402 alo = 141, ahi = 1101
2025-07-01 03:01:15.418 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:15.426 blo = 141, bhi = 1101
2025-07-01 03:01:15.434
2025-07-01 03:01:15.453 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:15.460 g = []
2025-07-01 03:01:15.474 if alo < ahi:
2025-07-01 03:01:15.482 if blo < bhi:
2025-07-01 03:01:15.498 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:15.510 else:
2025-07-01 03:01:15.522 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:15.533 elif blo < bhi:
2025-07-01 03:01:15.546 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:15.561
2025-07-01 03:01:15.573 >       yield from g
2025-07-01 03:01:15.585
2025-07-01 03:01:15.599 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:15.606 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:15.616
2025-07-01 03:01:15.629 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:15.636 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:15.642 alo = 141, ahi = 1101
2025-07-01 03:01:15.648 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:15.654 blo = 141, bhi = 1101
2025-07-01 03:01:15.660
2025-07-01 03:01:15.666 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:15.670 r"""
2025-07-01 03:01:15.677 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:15.682 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:15.686 synch point, and intraline difference marking is done on the
2025-07-01 03:01:15.691 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:15.695
2025-07-01 03:01:15.699 Example:
2025-07-01 03:01:15.703
2025-07-01 03:01:15.708 >>> d = Differ()
2025-07-01 03:01:15.712 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:15.717 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:15.721 >>> print(''.join(results), end="")
2025-07-01 03:01:15.726 - abcDefghiJkl
2025-07-01 03:01:15.736 + abcdefGhijkl
2025-07-01 03:01:15.754 """
2025-07-01 03:01:15.762
2025-07-01 03:01:15.782 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:15.794 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:15.810 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:15.822 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:15.834 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:15.842
2025-07-01 03:01:15.854 # search for the pair that matches best without being identical
2025-07-01 03:01:15.864 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:15.878 # on junk -- unless we have to)
2025-07-01 03:01:15.888 for j in range(blo, bhi):
2025-07-01 03:01:15.902 bj = b[j]
2025-07-01 03:01:15.913 cruncher.set_seq2(bj)
2025-07-01 03:01:15.930 for i in range(alo, ahi):
2025-07-01 03:01:15.938 ai = a[i]
2025-07-01 03:01:15.950 if ai == bj:
2025-07-01 03:01:15.958 if eqi is None:
2025-07-01 03:01:15.970 eqi, eqj = i, j
2025-07-01 03:01:15.977 continue
2025-07-01 03:01:15.992 cruncher.set_seq1(ai)
2025-07-01 03:01:16.003 # computing similarity is expensive, so use the quick
2025-07-01 03:01:16.018 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:16.030 # compares by a factor of 3.
2025-07-01 03:01:16.042 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:16.050 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:16.066 # of the computation is cached by cruncher
2025-07-01 03:01:16.078 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:16.086 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:16.096 cruncher.ratio() > best_ratio:
2025-07-01 03:01:16.110 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:16.120 if best_ratio < cutoff:
2025-07-01 03:01:16.134 # no non-identical "pretty close" pair
2025-07-01 03:01:16.139 if eqi is None:
2025-07-01 03:01:16.154 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:16.166 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:16.174 return
2025-07-01 03:01:16.186 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:16.194 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:16.206 else:
2025-07-01 03:01:16.214 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:16.222 eqi = None
2025-07-01 03:01:16.236
2025-07-01 03:01:16.246 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:16.258 # identical
2025-07-01 03:01:16.272
2025-07-01 03:01:16.282 # pump out diffs from before the synch point
2025-07-01 03:01:16.294 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:16.306
2025-07-01 03:01:16.318 # do intraline marking on the synch pair
2025-07-01 03:01:16.330 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:16.338 if eqi is None:
2025-07-01 03:01:16.351 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:16.366 atags = btags = ""
2025-07-01 03:01:16.378 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:16.390 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:16.402 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:16.418 if tag == 'replace':
2025-07-01 03:01:16.434 atags += '^' * la
2025-07-01 03:01:16.450 btags += '^' * lb
2025-07-01 03:01:16.458 elif tag == 'delete':
2025-07-01 03:01:16.474 atags += '-' * la
2025-07-01 03:01:16.490 elif tag == 'insert':
2025-07-01 03:01:16.498 btags += '+' * lb
2025-07-01 03:01:16.509 elif tag == 'equal':
2025-07-01 03:01:16.518 atags += ' ' * la
2025-07-01 03:01:16.534 btags += ' ' * lb
2025-07-01 03:01:16.546 else:
2025-07-01 03:01:16.562 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:16.570 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:16.583 else:
2025-07-01 03:01:16.594 # the synch pair is identical
2025-07-01 03:01:16.607 yield '  ' + aelt
2025-07-01 03:01:16.618
2025-07-01 03:01:16.630 # pump out diffs from after the synch point
2025-07-01 03:01:16.645 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:16.658
2025-07-01 03:01:16.666 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:16.678 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:16.694
2025-07-01 03:01:16.702 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:16.718 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:16.726 alo = 142, ahi = 1101
2025-07-01 03:01:16.742 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:16.750 blo = 142, bhi = 1101
2025-07-01 03:01:16.766
2025-07-01 03:01:16.774 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:16.782 g = []
2025-07-01 03:01:16.794 if alo < ahi:
2025-07-01 03:01:16.802 if blo < bhi:
2025-07-01 03:01:16.818 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:16.826 else:
2025-07-01 03:01:16.834 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:16.843 elif blo < bhi:
2025-07-01 03:01:16.857 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:16.866
2025-07-01 03:01:16.878 >       yield from g
2025-07-01 03:01:16.890
2025-07-01 03:01:16.902 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:16.916 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:16.930
2025-07-01 03:01:16.942 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:16.953 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:16.962 alo = 142, ahi = 1101
2025-07-01 03:01:16.982 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:16.990 blo = 142, bhi = 1101
2025-07-01 03:01:17.004
2025-07-01 03:01:17.021 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:17.030 r"""
2025-07-01 03:01:17.042 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:17.057 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:17.066 synch point, and intraline difference marking is done on the
2025-07-01 03:01:17.079 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:17.090
2025-07-01 03:01:17.098 Example:
2025-07-01 03:01:17.106
2025-07-01 03:01:17.118 >>> d = Differ()
2025-07-01 03:01:17.125 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:17.138 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:17.146 >>> print(''.join(results), end="")
2025-07-01 03:01:17.153 - abcDefghiJkl
2025-07-01 03:01:17.178 + abcdefGhijkl
2025-07-01 03:01:17.198 """
2025-07-01 03:01:17.209
2025-07-01 03:01:17.227 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:17.242 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:17.251 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:17.262 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:17.267 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:17.273
2025-07-01 03:01:17.279 # search for the pair that matches best without being identical
2025-07-01 03:01:17.285 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:17.291 # on junk -- unless we have to)
2025-07-01 03:01:17.297 for j in range(blo, bhi):
2025-07-01 03:01:17.302 bj = b[j]
2025-07-01 03:01:17.310 cruncher.set_seq2(bj)
2025-07-01 03:01:17.316 for i in range(alo, ahi):
2025-07-01 03:01:17.323 ai = a[i]
2025-07-01 03:01:17.334 if ai == bj:
2025-07-01 03:01:17.350 if eqi is None:
2025-07-01 03:01:17.358 eqi, eqj = i, j
2025-07-01 03:01:17.366 continue
2025-07-01 03:01:17.378 cruncher.set_seq1(ai)
2025-07-01 03:01:17.386 # computing similarity is expensive, so use the quick
2025-07-01 03:01:17.398 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:17.417 # compares by a factor of 3.
2025-07-01 03:01:17.434 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:17.450 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:17.466 # of the computation is cached by cruncher
2025-07-01 03:01:17.474 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:17.486 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:17.498 cruncher.ratio() > best_ratio:
2025-07-01 03:01:17.506 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:17.518 if best_ratio < cutoff:
2025-07-01 03:01:17.530 # no non-identical "pretty close" pair
2025-07-01 03:01:17.538 if eqi is None:
2025-07-01 03:01:17.550 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:17.562 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:17.570 return
2025-07-01 03:01:17.581 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:17.594 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:17.606 else:
2025-07-01 03:01:17.618 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:17.630 eqi = None
2025-07-01 03:01:17.638
2025-07-01 03:01:17.650 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:17.657 # identical
2025-07-01 03:01:17.670
2025-07-01 03:01:17.676 # pump out diffs from before the synch point
2025-07-01 03:01:17.680 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:17.685
2025-07-01 03:01:17.689 # do intraline marking on the synch pair
2025-07-01 03:01:17.694 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:17.699 if eqi is None:
2025-07-01 03:01:17.704 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:17.709 atags = btags = ""
2025-07-01 03:01:17.714 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:17.719 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:17.724 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:17.729 if tag == 'replace':
2025-07-01 03:01:17.735 atags += '^' * la
2025-07-01 03:01:17.739 btags += '^' * lb
2025-07-01 03:01:17.744 elif tag == 'delete':
2025-07-01 03:01:17.748 atags += '-' * la
2025-07-01 03:01:17.753 elif tag == 'insert':
2025-07-01 03:01:17.757 btags += '+' * lb
2025-07-01 03:01:17.761 elif tag == 'equal':
2025-07-01 03:01:17.766 atags += ' ' * la
2025-07-01 03:01:17.770 btags += ' ' * lb
2025-07-01 03:01:17.774 else:
2025-07-01 03:01:17.779 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:17.783 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:17.788 else:
2025-07-01 03:01:17.792 # the synch pair is identical
2025-07-01 03:01:17.796 yield '  ' + aelt
2025-07-01 03:01:17.801
2025-07-01 03:01:17.805 # pump out diffs from after the synch point
2025-07-01 03:01:17.810 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:17.814
2025-07-01 03:01:17.819 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:17.824 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:17.829
2025-07-01 03:01:17.833 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:17.838 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:17.843 alo = 143, ahi = 1101
2025-07-01 03:01:17.848 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:17.852 blo = 143, bhi = 1101
2025-07-01 03:01:17.857
2025-07-01 03:01:17.861 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:17.865 g = []
2025-07-01 03:01:17.870 if alo < ahi:
2025-07-01 03:01:17.874 if blo < bhi:
2025-07-01 03:01:17.879 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:17.883 else:
2025-07-01 03:01:17.887 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:17.892 elif blo < bhi:
2025-07-01 03:01:17.896 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:17.900
2025-07-01 03:01:17.905 >       yield from g
2025-07-01 03:01:17.910
2025-07-01 03:01:17.915 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:17.920 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:17.924
2025-07-01 03:01:17.930 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:17.934 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:17.939 alo = 143, ahi = 1101
2025-07-01 03:01:17.944 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:17.948 blo = 143, bhi = 1101
2025-07-01 03:01:17.952
2025-07-01 03:01:17.957 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:17.961 r"""
2025-07-01 03:01:17.966 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:17.974 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:17.982 synch point, and intraline difference marking is done on the
2025-07-01 03:01:17.990 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:17.995
2025-07-01 03:01:17.999 Example:
2025-07-01 03:01:18.004
2025-07-01 03:01:18.008 >>> d = Differ()
2025-07-01 03:01:18.013 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:18.017 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:18.022 >>> print(''.join(results), end="")
2025-07-01 03:01:18.026 - abcDefghiJkl
2025-07-01 03:01:18.035 + abcdefGhijkl
2025-07-01 03:01:18.044 """
2025-07-01 03:01:18.048
2025-07-01 03:01:18.053 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:18.057 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:18.062 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:18.066 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:18.071 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:18.075
2025-07-01 03:01:18.080 # search for the pair that matches best without being identical
2025-07-01 03:01:18.084 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:18.089 # on junk -- unless we have to)
2025-07-01 03:01:18.093 for j in range(blo, bhi):
2025-07-01 03:01:18.098 bj = b[j]
2025-07-01 03:01:18.102 cruncher.set_seq2(bj)
2025-07-01 03:01:18.106 for i in range(alo, ahi):
2025-07-01 03:01:18.111 ai = a[i]
2025-07-01 03:01:18.115 if ai == bj:
2025-07-01 03:01:18.119 if eqi is None:
2025-07-01 03:01:18.124 eqi, eqj = i, j
2025-07-01 03:01:18.128 continue
2025-07-01 03:01:18.133 cruncher.set_seq1(ai)
2025-07-01 03:01:18.137 # computing similarity is expensive, so use the quick
2025-07-01 03:01:18.142 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:18.146 # compares by a factor of 3.
2025-07-01 03:01:18.151 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:18.155 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:18.160 # of the computation is cached by cruncher
2025-07-01 03:01:18.164 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:18.169 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:18.173 cruncher.ratio() > best_ratio:
2025-07-01 03:01:18.179 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:18.183 if best_ratio < cutoff:
2025-07-01 03:01:18.188 # no non-identical "pretty close" pair
2025-07-01 03:01:18.192 if eqi is None:
2025-07-01 03:01:18.197 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:18.201 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:18.206 return
2025-07-01 03:01:18.210 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:18.215 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:18.219 else:
2025-07-01 03:01:18.224 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:18.229 eqi = None
2025-07-01 03:01:18.233
2025-07-01 03:01:18.238 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:18.242 # identical
2025-07-01 03:01:18.247
2025-07-01 03:01:18.251 # pump out diffs from before the synch point
2025-07-01 03:01:18.256 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:18.262
2025-07-01 03:01:18.266 # do intraline marking on the synch pair
2025-07-01 03:01:18.271 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:18.275 if eqi is None:
2025-07-01 03:01:18.281 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:18.285 atags = btags = ""
2025-07-01 03:01:18.290 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:18.295 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:18.299 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:18.304 if tag == 'replace':
2025-07-01 03:01:18.308 atags += '^' * la
2025-07-01 03:01:18.313 btags += '^' * lb
2025-07-01 03:01:18.317 elif tag == 'delete':
2025-07-01 03:01:18.321 atags += '-' * la
2025-07-01 03:01:18.326 elif tag == 'insert':
2025-07-01 03:01:18.330 btags += '+' * lb
2025-07-01 03:01:18.335 elif tag == 'equal':
2025-07-01 03:01:18.339 atags += ' ' * la
2025-07-01 03:01:18.344 btags += ' ' * lb
2025-07-01 03:01:18.349 else:
2025-07-01 03:01:18.354 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:18.358 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:18.363 else:
2025-07-01 03:01:18.367 # the synch pair is identical
2025-07-01 03:01:18.371 yield '  ' + aelt
2025-07-01 03:01:18.376
2025-07-01 03:01:18.380 # pump out diffs from after the synch point
2025-07-01 03:01:18.385 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:18.389
2025-07-01 03:01:18.394 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:18.398 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:18.402
2025-07-01 03:01:18.407 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:18.411 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:18.417 alo = 144, ahi = 1101
2025-07-01 03:01:18.423 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:18.429 blo = 144, bhi = 1101
2025-07-01 03:01:18.435
2025-07-01 03:01:18.441 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:18.446 g = []
2025-07-01 03:01:18.450 if alo < ahi:
2025-07-01 03:01:18.455 if blo < bhi:
2025-07-01 03:01:18.460 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:18.465 else:
2025-07-01 03:01:18.470 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:18.474 elif blo < bhi:
2025-07-01 03:01:18.479 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:18.483
2025-07-01 03:01:18.488 >       yield from g
2025-07-01 03:01:18.492
2025-07-01 03:01:18.497 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:18.501 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:18.505
2025-07-01 03:01:18.510 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:18.516 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:18.521 alo = 144, ahi = 1101
2025-07-01 03:01:18.526 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:18.530 blo = 144, bhi = 1101
2025-07-01 03:01:18.535
2025-07-01 03:01:18.540 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:18.545 r"""
2025-07-01 03:01:18.549 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:18.554 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:18.558 synch point, and intraline difference marking is done on the
2025-07-01 03:01:18.563 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:18.567
2025-07-01 03:01:18.571 Example:
2025-07-01 03:01:18.576
2025-07-01 03:01:18.580 >>> d = Differ()
2025-07-01 03:01:18.584 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:18.589 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:18.593 >>> print(''.join(results), end="")
2025-07-01 03:01:18.597 - abcDefghiJkl
2025-07-01 03:01:18.606 + abcdefGhijkl
2025-07-01 03:01:18.614 """
2025-07-01 03:01:18.619
2025-07-01 03:01:18.629 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:18.635 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:18.639 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:18.644 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:18.648 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:18.653
2025-07-01 03:01:18.658 # search for the pair that matches best without being identical
2025-07-01 03:01:18.662 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:18.667 # on junk -- unless we have to)
2025-07-01 03:01:18.671 for j in range(blo, bhi):
2025-07-01 03:01:18.675 bj = b[j]
2025-07-01 03:01:18.680 cruncher.set_seq2(bj)
2025-07-01 03:01:18.684 for i in range(alo, ahi):
2025-07-01 03:01:18.689 ai = a[i]
2025-07-01 03:01:18.693 if ai == bj:
2025-07-01 03:01:18.698 if eqi is None:
2025-07-01 03:01:18.702 eqi, eqj = i, j
2025-07-01 03:01:18.706 continue
2025-07-01 03:01:18.711 cruncher.set_seq1(ai)
2025-07-01 03:01:18.715 # computing similarity is expensive, so use the quick
2025-07-01 03:01:18.719 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:18.724 # compares by a factor of 3.
2025-07-01 03:01:18.728 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:18.733 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:18.740 # of the computation is cached by cruncher
2025-07-01 03:01:18.745 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:18.749 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:18.754 cruncher.ratio() > best_ratio:
2025-07-01 03:01:18.759 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:18.763 if best_ratio < cutoff:
2025-07-01 03:01:18.768 # no non-identical "pretty close" pair
2025-07-01 03:01:18.772 if eqi is None:
2025-07-01 03:01:18.777 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:18.782 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:18.786 return
2025-07-01 03:01:18.790 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:18.795 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:18.799 else:
2025-07-01 03:01:18.804 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:18.808 eqi = None
2025-07-01 03:01:18.813
2025-07-01 03:01:18.818 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:18.822 # identical
2025-07-01 03:01:18.826
2025-07-01 03:01:18.831 # pump out diffs from before the synch point
2025-07-01 03:01:18.835 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:18.840
2025-07-01 03:01:18.844 # do intraline marking on the synch pair
2025-07-01 03:01:18.849 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:18.853 if eqi is None:
2025-07-01 03:01:18.858 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:18.862 atags = btags = ""
2025-07-01 03:01:18.867 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:18.872 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:18.876 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:18.881 if tag == 'replace':
2025-07-01 03:01:18.885 atags += '^' * la
2025-07-01 03:01:18.890 btags += '^' * lb
2025-07-01 03:01:18.894 elif tag == 'delete':
2025-07-01 03:01:18.898 atags += '-' * la
2025-07-01 03:01:18.903 elif tag == 'insert':
2025-07-01 03:01:18.907 btags += '+' * lb
2025-07-01 03:01:18.912 elif tag == 'equal':
2025-07-01 03:01:18.916 atags += ' ' * la
2025-07-01 03:01:18.921 btags += ' ' * lb
2025-07-01 03:01:18.925 else:
2025-07-01 03:01:18.929 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:18.934 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:18.938 else:
2025-07-01 03:01:18.942 # the synch pair is identical
2025-07-01 03:01:18.947 yield '  ' + aelt
2025-07-01 03:01:18.951
2025-07-01 03:01:18.955 # pump out diffs from after the synch point
2025-07-01 03:01:18.960 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:18.964
2025-07-01 03:01:18.968 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:18.973 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:18.977
2025-07-01 03:01:18.981 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:18.987 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:18.991 alo = 145, ahi = 1101
2025-07-01 03:01:18.996 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:19.000 blo = 145, bhi = 1101
2025-07-01 03:01:19.005
2025-07-01 03:01:19.009 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:19.013 g = []
2025-07-01 03:01:19.018 if alo < ahi:
2025-07-01 03:01:19.022 if blo < bhi:
2025-07-01 03:01:19.026 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:19.030 else:
2025-07-01 03:01:19.035 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:19.039 elif blo < bhi:
2025-07-01 03:01:19.043 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:19.047
2025-07-01 03:01:19.052 >       yield from g
2025-07-01 03:01:19.056
2025-07-01 03:01:19.060 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:19.065 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:19.069
2025-07-01 03:01:19.074 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:19.078 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:19.083 alo = 145, ahi = 1101
2025-07-01 03:01:19.087 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:19.091 blo = 145, bhi = 1101
2025-07-01 03:01:19.096
2025-07-01 03:01:19.100 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:19.104 r"""
2025-07-01 03:01:19.108 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:19.113 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:19.117 synch point, and intraline difference marking is done on the
2025-07-01 03:01:19.122 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:19.126
2025-07-01 03:01:19.130 Example:
2025-07-01 03:01:19.134
2025-07-01 03:01:19.139 >>> d = Differ()
2025-07-01 03:01:19.143 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:19.147 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:19.152 >>> print(''.join(results), end="")
2025-07-01 03:01:19.156 - abcDefghiJkl
2025-07-01 03:01:19.165 + abcdefGhijkl
2025-07-01 03:01:19.176 """
2025-07-01 03:01:19.182
2025-07-01 03:01:19.189 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:19.195 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:19.201 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:19.207 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:19.213 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:19.219
2025-07-01 03:01:19.225 # search for the pair that matches best without being identical
2025-07-01 03:01:19.231 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:19.236 # on junk -- unless we have to)
2025-07-01 03:01:19.242 for j in range(blo, bhi):
2025-07-01 03:01:19.248 bj = b[j]
2025-07-01 03:01:19.254 cruncher.set_seq2(bj)
2025-07-01 03:01:19.260 for i in range(alo, ahi):
2025-07-01 03:01:19.266 ai = a[i]
2025-07-01 03:01:19.272 if ai == bj:
2025-07-01 03:01:19.278 if eqi is None:
2025-07-01 03:01:19.284 eqi, eqj = i, j
2025-07-01 03:01:19.290 continue
2025-07-01 03:01:19.296 cruncher.set_seq1(ai)
2025-07-01 03:01:19.301 # computing similarity is expensive, so use the quick
2025-07-01 03:01:19.305 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:19.310 # compares by a factor of 3.
2025-07-01 03:01:19.314 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:19.319 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:19.323 # of the computation is cached by cruncher
2025-07-01 03:01:19.328 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:19.332 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:19.337 cruncher.ratio() > best_ratio:
2025-07-01 03:01:19.341 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:19.345 if best_ratio < cutoff:
2025-07-01 03:01:19.351 # no non-identical "pretty close" pair
2025-07-01 03:01:19.355 if eqi is None:
2025-07-01 03:01:19.360 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:19.365 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:19.369 return
2025-07-01 03:01:19.374 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:19.378 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:19.383 else:
2025-07-01 03:01:19.388 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:19.394 eqi = None
2025-07-01 03:01:19.400
2025-07-01 03:01:19.406 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:19.412 # identical
2025-07-01 03:01:19.418
2025-07-01 03:01:19.424 # pump out diffs from before the synch point
2025-07-01 03:01:19.430 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:19.436
2025-07-01 03:01:19.441 # do intraline marking on the synch pair
2025-07-01 03:01:19.446 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:19.451 if eqi is None:
2025-07-01 03:01:19.456 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:19.461 atags = btags = ""
2025-07-01 03:01:19.466 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:19.470 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:19.475 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:19.479 if tag == 'replace':
2025-07-01 03:01:19.484 atags += '^' * la
2025-07-01 03:01:19.488 btags += '^' * lb
2025-07-01 03:01:19.493 elif tag == 'delete':
2025-07-01 03:01:19.498 atags += '-' * la
2025-07-01 03:01:19.502 elif tag == 'insert':
2025-07-01 03:01:19.507 btags += '+' * lb
2025-07-01 03:01:19.511 elif tag == 'equal':
2025-07-01 03:01:19.516 atags += ' ' * la
2025-07-01 03:01:19.520 btags += ' ' * lb
2025-07-01 03:01:19.524 else:
2025-07-01 03:01:19.529 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:19.533 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:19.537 else:
2025-07-01 03:01:19.542 # the synch pair is identical
2025-07-01 03:01:19.546 yield '  ' + aelt
2025-07-01 03:01:19.550
2025-07-01 03:01:19.555 # pump out diffs from after the synch point
2025-07-01 03:01:19.559 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:19.564
2025-07-01 03:01:19.568 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:19.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:19.577
2025-07-01 03:01:19.582 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:19.587 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:19.592 alo = 146, ahi = 1101
2025-07-01 03:01:19.597 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:19.602 blo = 146, bhi = 1101
2025-07-01 03:01:19.606
2025-07-01 03:01:19.610 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:19.615 g = []
2025-07-01 03:01:19.619 if alo < ahi:
2025-07-01 03:01:19.623 if blo < bhi:
2025-07-01 03:01:19.628 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:19.632 else:
2025-07-01 03:01:19.637 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:19.641 elif blo < bhi:
2025-07-01 03:01:19.646 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:19.650
2025-07-01 03:01:19.655 >       yield from g
2025-07-01 03:01:19.659
2025-07-01 03:01:19.663 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:19.668 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:19.673
2025-07-01 03:01:19.678 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:19.683 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:19.687 alo = 146, ahi = 1101
2025-07-01 03:01:19.693 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:19.698 blo = 146, bhi = 1101
2025-07-01 03:01:19.702
2025-07-01 03:01:19.707 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:19.711 r"""
2025-07-01 03:01:19.716 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:19.721 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:19.725 synch point, and intraline difference marking is done on the
2025-07-01 03:01:19.730 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:19.734
2025-07-01 03:01:19.739 Example:
2025-07-01 03:01:19.743
2025-07-01 03:01:19.748 >>> d = Differ()
2025-07-01 03:01:19.752 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:19.757 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:19.761 >>> print(''.join(results), end="")
2025-07-01 03:01:19.765 - abcDefghiJkl
2025-07-01 03:01:19.775 + abcdefGhijkl
2025-07-01 03:01:19.784 """
2025-07-01 03:01:19.789
2025-07-01 03:01:19.794 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:19.799 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:19.803 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:19.808 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:19.813 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:19.818
2025-07-01 03:01:19.824 # search for the pair that matches best without being identical
2025-07-01 03:01:19.830 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:19.835 # on junk -- unless we have to)
2025-07-01 03:01:19.840 for j in range(blo, bhi):
2025-07-01 03:01:19.845 bj = b[j]
2025-07-01 03:01:19.850 cruncher.set_seq2(bj)
2025-07-01 03:01:19.856 for i in range(alo, ahi):
2025-07-01 03:01:19.860 ai = a[i]
2025-07-01 03:01:19.865 if ai == bj:
2025-07-01 03:01:19.871 if eqi is None:
2025-07-01 03:01:19.875 eqi, eqj = i, j
2025-07-01 03:01:19.880 continue
2025-07-01 03:01:19.886 cruncher.set_seq1(ai)
2025-07-01 03:01:19.892 # computing similarity is expensive, so use the quick
2025-07-01 03:01:19.897 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:19.902 # compares by a factor of 3.
2025-07-01 03:01:19.906 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:19.911 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:19.915 # of the computation is cached by cruncher
2025-07-01 03:01:19.919 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:19.924 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:19.928 cruncher.ratio() > best_ratio:
2025-07-01 03:01:19.933 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:19.937 if best_ratio < cutoff:
2025-07-01 03:01:19.942 # no non-identical "pretty close" pair
2025-07-01 03:01:19.946 if eqi is None:
2025-07-01 03:01:19.951 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:19.955 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:19.960 return
2025-07-01 03:01:19.964 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:19.969 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:19.974 else:
2025-07-01 03:01:19.979 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:19.984 eqi = None
2025-07-01 03:01:19.989
2025-07-01 03:01:19.993 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:19.998 # identical
2025-07-01 03:01:20.003
2025-07-01 03:01:20.008 # pump out diffs from before the synch point
2025-07-01 03:01:20.013 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:20.018
2025-07-01 03:01:20.024 # do intraline marking on the synch pair
2025-07-01 03:01:20.030 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:20.035 if eqi is None:
2025-07-01 03:01:20.041 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:20.046 atags = btags = ""
2025-07-01 03:01:20.052 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:20.058 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:20.063 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:20.068 if tag == 'replace':
2025-07-01 03:01:20.072 atags += '^' * la
2025-07-01 03:01:20.077 btags += '^' * lb
2025-07-01 03:01:20.082 elif tag == 'delete':
2025-07-01 03:01:20.086 atags += '-' * la
2025-07-01 03:01:20.091 elif tag == 'insert':
2025-07-01 03:01:20.095 btags += '+' * lb
2025-07-01 03:01:20.100 elif tag == 'equal':
2025-07-01 03:01:20.105 atags += ' ' * la
2025-07-01 03:01:20.109 btags += ' ' * lb
2025-07-01 03:01:20.114 else:
2025-07-01 03:01:20.118 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:20.123 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:20.127 else:
2025-07-01 03:01:20.132 # the synch pair is identical
2025-07-01 03:01:20.136 yield '  ' + aelt
2025-07-01 03:01:20.141
2025-07-01 03:01:20.145 # pump out diffs from after the synch point
2025-07-01 03:01:20.150 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:20.154
2025-07-01 03:01:20.159 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:20.163 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:20.167
2025-07-01 03:01:20.172 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:20.177 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:20.182 alo = 147, ahi = 1101
2025-07-01 03:01:20.187 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:20.191 blo = 147, bhi = 1101
2025-07-01 03:01:20.196
2025-07-01 03:01:20.200 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:20.205 g = []
2025-07-01 03:01:20.209 if alo < ahi:
2025-07-01 03:01:20.214 if blo < bhi:
2025-07-01 03:01:20.218 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:20.222 else:
2025-07-01 03:01:20.228 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:20.233 elif blo < bhi:
2025-07-01 03:01:20.237 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:20.241
2025-07-01 03:01:20.246 >       yield from g
2025-07-01 03:01:20.250
2025-07-01 03:01:20.255 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:20.259 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:20.264
2025-07-01 03:01:20.268 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:20.273 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:20.278 alo = 147, ahi = 1101
2025-07-01 03:01:20.283 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:20.288 blo = 147, bhi = 1101
2025-07-01 03:01:20.292
2025-07-01 03:01:20.297 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:20.301 r"""
2025-07-01 03:01:20.306 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:20.311 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:20.315 synch point, and intraline difference marking is done on the
2025-07-01 03:01:20.319 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:20.323
2025-07-01 03:01:20.328 Example:
2025-07-01 03:01:20.332
2025-07-01 03:01:20.336 >>> d = Differ()
2025-07-01 03:01:20.341 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:20.345 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:20.350 >>> print(''.join(results), end="")
2025-07-01 03:01:20.354 - abcDefghiJkl
2025-07-01 03:01:20.363 + abcdefGhijkl
2025-07-01 03:01:20.372 """
2025-07-01 03:01:20.376
2025-07-01 03:01:20.381 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:20.386 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:20.392 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:20.397 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:20.402 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:20.406
2025-07-01 03:01:20.411 # search for the pair that matches best without being identical
2025-07-01 03:01:20.415 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:20.420 # on junk -- unless we have to)
2025-07-01 03:01:20.424 for j in range(blo, bhi):
2025-07-01 03:01:20.429 bj = b[j]
2025-07-01 03:01:20.433 cruncher.set_seq2(bj)
2025-07-01 03:01:20.438 for i in range(alo, ahi):
2025-07-01 03:01:20.442 ai = a[i]
2025-07-01 03:01:20.447 if ai == bj:
2025-07-01 03:01:20.451 if eqi is None:
2025-07-01 03:01:20.456 eqi, eqj = i, j
2025-07-01 03:01:20.460 continue
2025-07-01 03:01:20.464 cruncher.set_seq1(ai)
2025-07-01 03:01:20.469 # computing similarity is expensive, so use the quick
2025-07-01 03:01:20.473 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:20.478 # compares by a factor of 3.
2025-07-01 03:01:20.482 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:20.487 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:20.491 # of the computation is cached by cruncher
2025-07-01 03:01:20.495 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:20.500 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:20.504 cruncher.ratio() > best_ratio:
2025-07-01 03:01:20.509 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:20.513 if best_ratio < cutoff:
2025-07-01 03:01:20.518 # no non-identical "pretty close" pair
2025-07-01 03:01:20.523 if eqi is None:
2025-07-01 03:01:20.527 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:20.532 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:20.536 return
2025-07-01 03:01:20.540 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:20.545 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:20.549 else:
2025-07-01 03:01:20.554 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:20.558 eqi = None
2025-07-01 03:01:20.562
2025-07-01 03:01:20.567 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:20.572 # identical
2025-07-01 03:01:20.576
2025-07-01 03:01:20.581 # pump out diffs from before the synch point
2025-07-01 03:01:20.585 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:20.590
2025-07-01 03:01:20.594 # do intraline marking on the synch pair
2025-07-01 03:01:20.599 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:20.604 if eqi is None:
2025-07-01 03:01:20.608 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:20.613 atags = btags = ""
2025-07-01 03:01:20.617 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:20.622 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:20.626 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:20.631 if tag == 'replace':
2025-07-01 03:01:20.635 atags += '^' * la
2025-07-01 03:01:20.639 btags += '^' * lb
2025-07-01 03:01:20.644 elif tag == 'delete':
2025-07-01 03:01:20.648 atags += '-' * la
2025-07-01 03:01:20.653 elif tag == 'insert':
2025-07-01 03:01:20.657 btags += '+' * lb
2025-07-01 03:01:20.661 elif tag == 'equal':
2025-07-01 03:01:20.666 atags += ' ' * la
2025-07-01 03:01:20.670 btags += ' ' * lb
2025-07-01 03:01:20.675 else:
2025-07-01 03:01:20.679 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:20.683 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:20.688 else:
2025-07-01 03:01:20.693 # the synch pair is identical
2025-07-01 03:01:20.697 yield '  ' + aelt
2025-07-01 03:01:20.701
2025-07-01 03:01:20.706 # pump out diffs from after the synch point
2025-07-01 03:01:20.711 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:20.715
2025-07-01 03:01:20.719 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:20.724 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:20.728
2025-07-01 03:01:20.733 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:20.737 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:20.742 alo = 148, ahi = 1101
2025-07-01 03:01:20.747 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:20.751 blo = 148, bhi = 1101
2025-07-01 03:01:20.755
2025-07-01 03:01:20.760 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:20.764 g = []
2025-07-01 03:01:20.768 if alo < ahi:
2025-07-01 03:01:20.773 if blo < bhi:
2025-07-01 03:01:20.777 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:20.781 else:
2025-07-01 03:01:20.786 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:20.790 elif blo < bhi:
2025-07-01 03:01:20.794 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:20.799
2025-07-01 03:01:20.803 >       yield from g
2025-07-01 03:01:20.807
2025-07-01 03:01:20.811 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:20.816 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:20.820
2025-07-01 03:01:20.824 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:20.829 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:20.833 alo = 148, ahi = 1101
2025-07-01 03:01:20.838 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:20.842 blo = 148, bhi = 1101
2025-07-01 03:01:20.846
2025-07-01 03:01:20.851 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:20.855 r"""
2025-07-01 03:01:20.859 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:20.864 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:20.868 synch point, and intraline difference marking is done on the
2025-07-01 03:01:20.873 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:20.877
2025-07-01 03:01:20.881 Example:
2025-07-01 03:01:20.885
2025-07-01 03:01:20.890 >>> d = Differ()
2025-07-01 03:01:20.894 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:20.898 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:20.902 >>> print(''.join(results), end="")
2025-07-01 03:01:20.907 - abcDefghiJkl
2025-07-01 03:01:20.915 + abcdefGhijkl
2025-07-01 03:01:20.924 """
2025-07-01 03:01:20.928
2025-07-01 03:01:20.933 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:20.937 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:20.941 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:20.945 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:20.950 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:20.954
2025-07-01 03:01:20.959 # search for the pair that matches best without being identical
2025-07-01 03:01:20.963 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:20.967 # on junk -- unless we have to)
2025-07-01 03:01:20.971 for j in range(blo, bhi):
2025-07-01 03:01:20.976 bj = b[j]
2025-07-01 03:01:20.980 cruncher.set_seq2(bj)
2025-07-01 03:01:20.984 for i in range(alo, ahi):
2025-07-01 03:01:20.989 ai = a[i]
2025-07-01 03:01:20.993 if ai == bj:
2025-07-01 03:01:20.997 if eqi is None:
2025-07-01 03:01:21.002 eqi, eqj = i, j
2025-07-01 03:01:21.006 continue
2025-07-01 03:01:21.010 cruncher.set_seq1(ai)
2025-07-01 03:01:21.015 # computing similarity is expensive, so use the quick
2025-07-01 03:01:21.019 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:21.023 # compares by a factor of 3.
2025-07-01 03:01:21.028 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:21.033 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:21.037 # of the computation is cached by cruncher
2025-07-01 03:01:21.041 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:21.046 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:21.050 cruncher.ratio() > best_ratio:
2025-07-01 03:01:21.055 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:21.059 if best_ratio < cutoff:
2025-07-01 03:01:21.064 # no non-identical "pretty close" pair
2025-07-01 03:01:21.068 if eqi is None:
2025-07-01 03:01:21.072 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:21.077 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:21.081 return
2025-07-01 03:01:21.085 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:21.090 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:21.094 else:
2025-07-01 03:01:21.098 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:21.103 eqi = None
2025-07-01 03:01:21.107
2025-07-01 03:01:21.112 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:21.116 # identical
2025-07-01 03:01:21.120
2025-07-01 03:01:21.124 # pump out diffs from before the synch point
2025-07-01 03:01:21.129 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:21.133
2025-07-01 03:01:21.137 # do intraline marking on the synch pair
2025-07-01 03:01:21.142 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:21.146 if eqi is None:
2025-07-01 03:01:21.150 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:21.155 atags = btags = ""
2025-07-01 03:01:21.159 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:21.165 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:21.169 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:21.173 if tag == 'replace':
2025-07-01 03:01:21.178 atags += '^' * la
2025-07-01 03:01:21.182 btags += '^' * lb
2025-07-01 03:01:21.186 elif tag == 'delete':
2025-07-01 03:01:21.191 atags += '-' * la
2025-07-01 03:01:21.195 elif tag == 'insert':
2025-07-01 03:01:21.199 btags += '+' * lb
2025-07-01 03:01:21.204 elif tag == 'equal':
2025-07-01 03:01:21.208 atags += ' ' * la
2025-07-01 03:01:21.212 btags += ' ' * lb
2025-07-01 03:01:21.217 else:
2025-07-01 03:01:21.221 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:21.225 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:21.230 else:
2025-07-01 03:01:21.234 # the synch pair is identical
2025-07-01 03:01:21.238 yield '  ' + aelt
2025-07-01 03:01:21.243
2025-07-01 03:01:21.247 # pump out diffs from after the synch point
2025-07-01 03:01:21.252 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:21.257
2025-07-01 03:01:21.262 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:21.266 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:21.271
2025-07-01 03:01:21.275 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:21.280 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:21.284 alo = 149, ahi = 1101
2025-07-01 03:01:21.289 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:21.294 blo = 149, bhi = 1101
2025-07-01 03:01:21.298
2025-07-01 03:01:21.303 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:21.307 g = []
2025-07-01 03:01:21.312 if alo < ahi:
2025-07-01 03:01:21.316 if blo < bhi:
2025-07-01 03:01:21.320 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:21.325 else:
2025-07-01 03:01:21.329 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:21.334 elif blo < bhi:
2025-07-01 03:01:21.338 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:21.343
2025-07-01 03:01:21.347 >       yield from g
2025-07-01 03:01:21.352
2025-07-01 03:01:21.356 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:21.361 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:21.365
2025-07-01 03:01:21.370 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:21.374 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:21.379 alo = 149, ahi = 1101
2025-07-01 03:01:21.384 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:21.388 blo = 149, bhi = 1101
2025-07-01 03:01:21.392
2025-07-01 03:01:21.397 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:21.401 r"""
2025-07-01 03:01:21.405 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:21.410 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:21.414 synch point, and intraline difference marking is done on the
2025-07-01 03:01:21.419 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:21.423
2025-07-01 03:01:21.427 Example:
2025-07-01 03:01:21.431
2025-07-01 03:01:21.436 >>> d = Differ()
2025-07-01 03:01:21.441 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:21.446 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:21.450 >>> print(''.join(results), end="")
2025-07-01 03:01:21.454 - abcDefghiJkl
2025-07-01 03:01:21.463 + abcdefGhijkl
2025-07-01 03:01:21.471 """
2025-07-01 03:01:21.476
2025-07-01 03:01:21.480 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:21.484 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:21.488 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:21.493 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:21.497 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:21.501
2025-07-01 03:01:21.506 # search for the pair that matches best without being identical
2025-07-01 03:01:21.510 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:21.515 # on junk -- unless we have to)
2025-07-01 03:01:21.519 for j in range(blo, bhi):
2025-07-01 03:01:21.523 bj = b[j]
2025-07-01 03:01:21.528 cruncher.set_seq2(bj)
2025-07-01 03:01:21.532 for i in range(alo, ahi):
2025-07-01 03:01:21.536 ai = a[i]
2025-07-01 03:01:21.541 if ai == bj:
2025-07-01 03:01:21.545 if eqi is None:
2025-07-01 03:01:21.550 eqi, eqj = i, j
2025-07-01 03:01:21.554 continue
2025-07-01 03:01:21.559 cruncher.set_seq1(ai)
2025-07-01 03:01:21.564 # computing similarity is expensive, so use the quick
2025-07-01 03:01:21.568 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:21.573 # compares by a factor of 3.
2025-07-01 03:01:21.578 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:21.582 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:21.586 # of the computation is cached by cruncher
2025-07-01 03:01:21.591 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:21.596 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:21.601 cruncher.ratio() > best_ratio:
2025-07-01 03:01:21.606 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:21.610 if best_ratio < cutoff:
2025-07-01 03:01:21.614 # no non-identical "pretty close" pair
2025-07-01 03:01:21.619 if eqi is None:
2025-07-01 03:01:21.623 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:21.628 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:21.632 return
2025-07-01 03:01:21.637 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:21.641 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:21.645 else:
2025-07-01 03:01:21.650 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:21.654 eqi = None
2025-07-01 03:01:21.658
2025-07-01 03:01:21.663 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:21.667 # identical
2025-07-01 03:01:21.671
2025-07-01 03:01:21.676 # pump out diffs from before the synch point
2025-07-01 03:01:21.681 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:21.685
2025-07-01 03:01:21.690 # do intraline marking on the synch pair
2025-07-01 03:01:21.694 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:21.699 if eqi is None:
2025-07-01 03:01:21.703 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:21.708 atags = btags = ""
2025-07-01 03:01:21.712 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:21.716 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:21.721 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:21.725 if tag == 'replace':
2025-07-01 03:01:21.730 atags += '^' * la
2025-07-01 03:01:21.734 btags += '^' * lb
2025-07-01 03:01:21.739 elif tag == 'delete':
2025-07-01 03:01:21.743 atags += '-' * la
2025-07-01 03:01:21.748 elif tag == 'insert':
2025-07-01 03:01:21.752 btags += '+' * lb
2025-07-01 03:01:21.756 elif tag == 'equal':
2025-07-01 03:01:21.761 atags += ' ' * la
2025-07-01 03:01:21.765 btags += ' ' * lb
2025-07-01 03:01:21.770 else:
2025-07-01 03:01:21.774 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:21.778 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:21.784 else:
2025-07-01 03:01:21.789 # the synch pair is identical
2025-07-01 03:01:21.793 yield '  ' + aelt
2025-07-01 03:01:21.797
2025-07-01 03:01:21.802 # pump out diffs from after the synch point
2025-07-01 03:01:21.806 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:21.810
2025-07-01 03:01:21.814 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:21.819 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:21.823
2025-07-01 03:01:21.827 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:21.832 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:21.836 alo = 150, ahi = 1101
2025-07-01 03:01:21.841 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:21.845 blo = 150, bhi = 1101
2025-07-01 03:01:21.850
2025-07-01 03:01:21.854 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:21.859 g = []
2025-07-01 03:01:21.863 if alo < ahi:
2025-07-01 03:01:21.868 if blo < bhi:
2025-07-01 03:01:21.872 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:21.877 else:
2025-07-01 03:01:21.881 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:21.885 elif blo < bhi:
2025-07-01 03:01:21.890 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:21.894
2025-07-01 03:01:21.898 >       yield from g
2025-07-01 03:01:21.903
2025-07-01 03:01:21.907 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:21.912 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:21.918
2025-07-01 03:01:21.923 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:21.928 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:21.933 alo = 150, ahi = 1101
2025-07-01 03:01:21.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:21.942 blo = 150, bhi = 1101
2025-07-01 03:01:21.946
2025-07-01 03:01:21.951 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:21.955 r"""
2025-07-01 03:01:21.960 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:21.965 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:21.969 synch point, and intraline difference marking is done on the
2025-07-01 03:01:21.974 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:21.978
2025-07-01 03:01:21.982 Example:
2025-07-01 03:01:21.987
2025-07-01 03:01:21.993 >>> d = Differ()
2025-07-01 03:01:21.998 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:22.003 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:22.008 >>> print(''.join(results), end="")
2025-07-01 03:01:22.012 - abcDefghiJkl
2025-07-01 03:01:22.022 + abcdefGhijkl
2025-07-01 03:01:22.030 """
2025-07-01 03:01:22.035
2025-07-01 03:01:22.040 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:22.045 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:22.050 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:22.055 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:22.060 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:22.064
2025-07-01 03:01:22.068 # search for the pair that matches best without being identical
2025-07-01 03:01:22.073 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:22.078 # on junk -- unless we have to)
2025-07-01 03:01:22.082 for j in range(blo, bhi):
2025-07-01 03:01:22.087 bj = b[j]
2025-07-01 03:01:22.091 cruncher.set_seq2(bj)
2025-07-01 03:01:22.096 for i in range(alo, ahi):
2025-07-01 03:01:22.100 ai = a[i]
2025-07-01 03:01:22.105 if ai == bj:
2025-07-01 03:01:22.110 if eqi is None:
2025-07-01 03:01:22.116 eqi, eqj = i, j
2025-07-01 03:01:22.121 continue
2025-07-01 03:01:22.142 cruncher.set_seq1(ai)
2025-07-01 03:01:22.150 # computing similarity is expensive, so use the quick
2025-07-01 03:01:22.165 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:22.182 # compares by a factor of 3.
2025-07-01 03:01:22.194 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:22.206 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:22.215 # of the computation is cached by cruncher
2025-07-01 03:01:22.220 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:22.224 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:22.229 cruncher.ratio() > best_ratio:
2025-07-01 03:01:22.233 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:22.238 if best_ratio < cutoff:
2025-07-01 03:01:22.244 # no non-identical "pretty close" pair
2025-07-01 03:01:22.251 if eqi is None:
2025-07-01 03:01:22.257 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:22.265 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:22.271 return
2025-07-01 03:01:22.278 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:22.284 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:22.289 else:
2025-07-01 03:01:22.295 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:22.299 eqi = None
2025-07-01 03:01:22.306
2025-07-01 03:01:22.310 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:22.315 # identical
2025-07-01 03:01:22.319
2025-07-01 03:01:22.325 # pump out diffs from before the synch point
2025-07-01 03:01:22.329 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:22.334
2025-07-01 03:01:22.339 # do intraline marking on the synch pair
2025-07-01 03:01:22.343 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:22.348 if eqi is None:
2025-07-01 03:01:22.354 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:22.358 atags = btags = ""
2025-07-01 03:01:22.363 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:22.367 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:22.371 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:22.376 if tag == 'replace':
2025-07-01 03:01:22.381 atags += '^' * la
2025-07-01 03:01:22.387 btags += '^' * lb
2025-07-01 03:01:22.391 elif tag == 'delete':
2025-07-01 03:01:22.396 atags += '-' * la
2025-07-01 03:01:22.401 elif tag == 'insert':
2025-07-01 03:01:22.405 btags += '+' * lb
2025-07-01 03:01:22.410 elif tag == 'equal':
2025-07-01 03:01:22.414 atags += ' ' * la
2025-07-01 03:01:22.419 btags += ' ' * lb
2025-07-01 03:01:22.423 else:
2025-07-01 03:01:22.427 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:22.433 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:22.438 else:
2025-07-01 03:01:22.442 # the synch pair is identical
2025-07-01 03:01:22.447 yield '  ' + aelt
2025-07-01 03:01:22.451
2025-07-01 03:01:22.455 # pump out diffs from after the synch point
2025-07-01 03:01:22.460 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:22.464
2025-07-01 03:01:22.469 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:22.473 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:22.478
2025-07-01 03:01:22.482 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:22.487 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:22.491 alo = 151, ahi = 1101
2025-07-01 03:01:22.496 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:22.501 blo = 151, bhi = 1101
2025-07-01 03:01:22.505
2025-07-01 03:01:22.509 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:22.514 g = []
2025-07-01 03:01:22.518 if alo < ahi:
2025-07-01 03:01:22.523 if blo < bhi:
2025-07-01 03:01:22.527 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:22.531 else:
2025-07-01 03:01:22.536 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:22.540 elif blo < bhi:
2025-07-01 03:01:22.545 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:22.549
2025-07-01 03:01:22.553 >       yield from g
2025-07-01 03:01:22.557
2025-07-01 03:01:22.562 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:22.566 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:22.571
2025-07-01 03:01:22.575 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:22.580 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:22.584 alo = 151, ahi = 1101
2025-07-01 03:01:22.589 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:22.594 blo = 151, bhi = 1101
2025-07-01 03:01:22.598
2025-07-01 03:01:22.602 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:22.607 r"""
2025-07-01 03:01:22.611 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:22.615 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:22.620 synch point, and intraline difference marking is done on the
2025-07-01 03:01:22.625 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:22.629
2025-07-01 03:01:22.633 Example:
2025-07-01 03:01:22.638
2025-07-01 03:01:22.642 >>> d = Differ()
2025-07-01 03:01:22.647 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:22.652 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:22.657 >>> print(''.join(results), end="")
2025-07-01 03:01:22.661 - abcDefghiJkl
2025-07-01 03:01:22.670 + abcdefGhijkl
2025-07-01 03:01:22.680 """
2025-07-01 03:01:22.684
2025-07-01 03:01:22.688 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:22.693 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:22.698 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:22.702 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:22.707 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:22.711
2025-07-01 03:01:22.716 # search for the pair that matches best without being identical
2025-07-01 03:01:22.720 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:22.725 # on junk -- unless we have to)
2025-07-01 03:01:22.729 for j in range(blo, bhi):
2025-07-01 03:01:22.734 bj = b[j]
2025-07-01 03:01:22.738 cruncher.set_seq2(bj)
2025-07-01 03:01:22.743 for i in range(alo, ahi):
2025-07-01 03:01:22.747 ai = a[i]
2025-07-01 03:01:22.752 if ai == bj:
2025-07-01 03:01:22.757 if eqi is None:
2025-07-01 03:01:22.761 eqi, eqj = i, j
2025-07-01 03:01:22.766 continue
2025-07-01 03:01:22.771 cruncher.set_seq1(ai)
2025-07-01 03:01:22.775 # computing similarity is expensive, so use the quick
2025-07-01 03:01:22.780 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:22.784 # compares by a factor of 3.
2025-07-01 03:01:22.789 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:22.794 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:22.798 # of the computation is cached by cruncher
2025-07-01 03:01:22.803 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:22.807 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:22.812 cruncher.ratio() > best_ratio:
2025-07-01 03:01:22.817 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:22.821 if best_ratio < cutoff:
2025-07-01 03:01:22.826 # no non-identical "pretty close" pair
2025-07-01 03:01:22.831 if eqi is None:
2025-07-01 03:01:22.835 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:22.839 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:22.844 return
2025-07-01 03:01:22.849 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:22.853 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:22.858 else:
2025-07-01 03:01:22.863 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:22.869 eqi = None
2025-07-01 03:01:22.873
2025-07-01 03:01:22.878 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:22.882 # identical
2025-07-01 03:01:22.887
2025-07-01 03:01:22.892 # pump out diffs from before the synch point
2025-07-01 03:01:22.898 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:22.903
2025-07-01 03:01:22.907 # do intraline marking on the synch pair
2025-07-01 03:01:22.912 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:22.917 if eqi is None:
2025-07-01 03:01:22.922 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:22.927 atags = btags = ""
2025-07-01 03:01:22.931 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:22.936 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:22.940 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:22.945 if tag == 'replace':
2025-07-01 03:01:22.950 atags += '^' * la
2025-07-01 03:01:22.955 btags += '^' * lb
2025-07-01 03:01:22.961 elif tag == 'delete':
2025-07-01 03:01:22.967 atags += '-' * la
2025-07-01 03:01:22.972 elif tag == 'insert':
2025-07-01 03:01:22.976 btags += '+' * lb
2025-07-01 03:01:22.981 elif tag == 'equal':
2025-07-01 03:01:22.985 atags += ' ' * la
2025-07-01 03:01:22.990 btags += ' ' * lb
2025-07-01 03:01:22.994 else:
2025-07-01 03:01:22.998 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:23.003 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:23.008 else:
2025-07-01 03:01:23.013 # the synch pair is identical
2025-07-01 03:01:23.017 yield '  ' + aelt
2025-07-01 03:01:23.022
2025-07-01 03:01:23.027 # pump out diffs from after the synch point
2025-07-01 03:01:23.031 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:23.036
2025-07-01 03:01:23.040 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:23.045 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:23.049
2025-07-01 03:01:23.054 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:23.058 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:23.063 alo = 152, ahi = 1101
2025-07-01 03:01:23.068 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:23.073 blo = 152, bhi = 1101
2025-07-01 03:01:23.077
2025-07-01 03:01:23.082 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:23.086 g = []
2025-07-01 03:01:23.091 if alo < ahi:
2025-07-01 03:01:23.095 if blo < bhi:
2025-07-01 03:01:23.100 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:23.105 else:
2025-07-01 03:01:23.109 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:23.113 elif blo < bhi:
2025-07-01 03:01:23.118 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:23.122
2025-07-01 03:01:23.127 >       yield from g
2025-07-01 03:01:23.131
2025-07-01 03:01:23.136 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:23.140 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:23.145
2025-07-01 03:01:23.149 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:23.154 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:23.158 alo = 152, ahi = 1101
2025-07-01 03:01:23.164 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:23.168 blo = 152, bhi = 1101
2025-07-01 03:01:23.172
2025-07-01 03:01:23.178 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:23.182 r"""
2025-07-01 03:01:23.187 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:23.191 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:23.196 synch point, and intraline difference marking is done on the
2025-07-01 03:01:23.200 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:23.205
2025-07-01 03:01:23.209 Example:
2025-07-01 03:01:23.213
2025-07-01 03:01:23.218 >>> d = Differ()
2025-07-01 03:01:23.222 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:23.227 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:23.231 >>> print(''.join(results), end="")
2025-07-01 03:01:23.236 - abcDefghiJkl
2025-07-01 03:01:23.244 + abcdefGhijkl
2025-07-01 03:01:23.253 """
2025-07-01 03:01:23.257
2025-07-01 03:01:23.262 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:23.266 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:23.271 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:23.275 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:23.280 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:23.285
2025-07-01 03:01:23.289 # search for the pair that matches best without being identical
2025-07-01 03:01:23.294 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:23.298 # on junk -- unless we have to)
2025-07-01 03:01:23.303 for j in range(blo, bhi):
2025-07-01 03:01:23.309 bj = b[j]
2025-07-01 03:01:23.313 cruncher.set_seq2(bj)
2025-07-01 03:01:23.317 for i in range(alo, ahi):
2025-07-01 03:01:23.322 ai = a[i]
2025-07-01 03:01:23.326 if ai == bj:
2025-07-01 03:01:23.330 if eqi is None:
2025-07-01 03:01:23.335 eqi, eqj = i, j
2025-07-01 03:01:23.340 continue
2025-07-01 03:01:23.344 cruncher.set_seq1(ai)
2025-07-01 03:01:23.348 # computing similarity is expensive, so use the quick
2025-07-01 03:01:23.353 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:23.358 # compares by a factor of 3.
2025-07-01 03:01:23.363 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:23.367 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:23.373 # of the computation is cached by cruncher
2025-07-01 03:01:23.378 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:23.382 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:23.387 cruncher.ratio() > best_ratio:
2025-07-01 03:01:23.392 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:23.396 if best_ratio < cutoff:
2025-07-01 03:01:23.401 # no non-identical "pretty close" pair
2025-07-01 03:01:23.407 if eqi is None:
2025-07-01 03:01:23.414 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:23.424 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:23.429 return
2025-07-01 03:01:23.434 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:23.440 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:23.445 else:
2025-07-01 03:01:23.450 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:23.456 eqi = None
2025-07-01 03:01:23.462
2025-07-01 03:01:23.467 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:23.473 # identical
2025-07-01 03:01:23.478
2025-07-01 03:01:23.483 # pump out diffs from before the synch point
2025-07-01 03:01:23.488 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:23.494
2025-07-01 03:01:23.499 # do intraline marking on the synch pair
2025-07-01 03:01:23.505 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:23.510 if eqi is None:
2025-07-01 03:01:23.517 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:23.522 atags = btags = ""
2025-07-01 03:01:23.528 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:23.532 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:23.538 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:23.542 if tag == 'replace':
2025-07-01 03:01:23.547 atags += '^' * la
2025-07-01 03:01:23.551 btags += '^' * lb
2025-07-01 03:01:23.556 elif tag == 'delete':
2025-07-01 03:01:23.560 atags += '-' * la
2025-07-01 03:01:23.565 elif tag == 'insert':
2025-07-01 03:01:23.570 btags += '+' * lb
2025-07-01 03:01:23.575 elif tag == 'equal':
2025-07-01 03:01:23.580 atags += ' ' * la
2025-07-01 03:01:23.585 btags += ' ' * lb
2025-07-01 03:01:23.590 else:
2025-07-01 03:01:23.595 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:23.600 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:23.605 else:
2025-07-01 03:01:23.609 # the synch pair is identical
2025-07-01 03:01:23.613 yield '  ' + aelt
2025-07-01 03:01:23.618
2025-07-01 03:01:23.622 # pump out diffs from after the synch point
2025-07-01 03:01:23.627 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:23.631
2025-07-01 03:01:23.636 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:23.641 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:23.645
2025-07-01 03:01:23.649 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:23.654 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:23.659 alo = 153, ahi = 1101
2025-07-01 03:01:23.664 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:23.668 blo = 153, bhi = 1101
2025-07-01 03:01:23.673
2025-07-01 03:01:23.677 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:23.685 g = []
2025-07-01 03:01:23.690 if alo < ahi:
2025-07-01 03:01:23.694 if blo < bhi:
2025-07-01 03:01:23.699 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:23.704 else:
2025-07-01 03:01:23.708 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:23.713 elif blo < bhi:
2025-07-01 03:01:23.717 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:23.722
2025-07-01 03:01:23.727 >       yield from g
2025-07-01 03:01:23.732
2025-07-01 03:01:23.736 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:23.741 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:23.746
2025-07-01 03:01:23.751 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:23.757 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:23.762 alo = 153, ahi = 1101
2025-07-01 03:01:23.767 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:23.772 blo = 153, bhi = 1101
2025-07-01 03:01:23.776
2025-07-01 03:01:23.781 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:23.786 r"""
2025-07-01 03:01:23.791 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:23.796 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:23.800 synch point, and intraline difference marking is done on the
2025-07-01 03:01:23.805 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:23.809
2025-07-01 03:01:23.814 Example:
2025-07-01 03:01:23.818
2025-07-01 03:01:23.822 >>> d = Differ()
2025-07-01 03:01:23.827 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:23.832 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:23.836 >>> print(''.join(results), end="")
2025-07-01 03:01:23.841 - abcDefghiJkl
2025-07-01 03:01:23.850 + abcdefGhijkl
2025-07-01 03:01:23.859 """
2025-07-01 03:01:23.863
2025-07-01 03:01:23.868 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:23.872 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:23.877 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:23.881 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:23.886 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:23.891
2025-07-01 03:01:23.895 # search for the pair that matches best without being identical
2025-07-01 03:01:23.899 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:23.904 # on junk -- unless we have to)
2025-07-01 03:01:23.908 for j in range(blo, bhi):
2025-07-01 03:01:23.913 bj = b[j]
2025-07-01 03:01:23.917 cruncher.set_seq2(bj)
2025-07-01 03:01:23.921 for i in range(alo, ahi):
2025-07-01 03:01:23.926 ai = a[i]
2025-07-01 03:01:23.930 if ai == bj:
2025-07-01 03:01:23.934 if eqi is None:
2025-07-01 03:01:23.939 eqi, eqj = i, j
2025-07-01 03:01:23.943 continue
2025-07-01 03:01:23.948 cruncher.set_seq1(ai)
2025-07-01 03:01:23.952 # computing similarity is expensive, so use the quick
2025-07-01 03:01:23.957 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:23.962 # compares by a factor of 3.
2025-07-01 03:01:23.966 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:23.971 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:23.975 # of the computation is cached by cruncher
2025-07-01 03:01:23.980 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:23.984 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:23.988 cruncher.ratio() > best_ratio:
2025-07-01 03:01:23.993 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:23.998 if best_ratio < cutoff:
2025-07-01 03:01:24.002 # no non-identical "pretty close" pair
2025-07-01 03:01:24.007 if eqi is None:
2025-07-01 03:01:24.011 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:24.016 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:24.020 return
2025-07-01 03:01:24.025 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:24.029 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:24.034 else:
2025-07-01 03:01:24.038 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:24.043 eqi = None
2025-07-01 03:01:24.048
2025-07-01 03:01:24.053 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:24.057 # identical
2025-07-01 03:01:24.062
2025-07-01 03:01:24.066 # pump out diffs from before the synch point
2025-07-01 03:01:24.071 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:24.076
2025-07-01 03:01:24.080 # do intraline marking on the synch pair
2025-07-01 03:01:24.085 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:24.089 if eqi is None:
2025-07-01 03:01:24.094 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:24.098 atags = btags = ""
2025-07-01 03:01:24.102 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:24.107 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:24.113 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:24.117 if tag == 'replace':
2025-07-01 03:01:24.126 atags += '^' * la
2025-07-01 03:01:24.146 btags += '^' * lb
2025-07-01 03:01:24.158 elif tag == 'delete':
2025-07-01 03:01:24.174 atags += '-' * la
2025-07-01 03:01:24.186 elif tag == 'insert':
2025-07-01 03:01:24.198 btags += '+' * lb
2025-07-01 03:01:24.210 elif tag == 'equal':
2025-07-01 03:01:24.225 atags += ' ' * la
2025-07-01 03:01:24.240 btags += ' ' * lb
2025-07-01 03:01:24.250 else:
2025-07-01 03:01:24.266 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:24.278 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:24.293 else:
2025-07-01 03:01:24.303 # the synch pair is identical
2025-07-01 03:01:24.316 yield '  ' + aelt
2025-07-01 03:01:24.333
2025-07-01 03:01:24.345 # pump out diffs from after the synch point
2025-07-01 03:01:24.362 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:24.377
2025-07-01 03:01:24.389 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:24.405 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:24.417
2025-07-01 03:01:24.426 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:24.441 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:24.454 alo = 154, ahi = 1101
2025-07-01 03:01:24.469 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:24.485 blo = 154, bhi = 1101
2025-07-01 03:01:24.494
2025-07-01 03:01:24.511 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:24.525 g = []
2025-07-01 03:01:24.537 if alo < ahi:
2025-07-01 03:01:24.554 if blo < bhi:
2025-07-01 03:01:24.566 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:24.578 else:
2025-07-01 03:01:24.596 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:24.606 elif blo < bhi:
2025-07-01 03:01:24.621 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:24.638
2025-07-01 03:01:24.650 >       yield from g
2025-07-01 03:01:24.662
2025-07-01 03:01:24.679 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:24.690 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:24.698
2025-07-01 03:01:24.710 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:24.726 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:24.738 alo = 154, ahi = 1101
2025-07-01 03:01:24.754 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:24.766 blo = 154, bhi = 1101
2025-07-01 03:01:24.774
2025-07-01 03:01:24.786 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:24.798 r"""
2025-07-01 03:01:24.806 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:24.818 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:24.830 synch point, and intraline difference marking is done on the
2025-07-01 03:01:24.840 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:24.850
2025-07-01 03:01:24.867 Example:
2025-07-01 03:01:24.878
2025-07-01 03:01:24.894 >>> d = Differ()
2025-07-01 03:01:24.902 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:24.918 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:24.930 >>> print(''.join(results), end="")
2025-07-01 03:01:24.938 - abcDefghiJkl
2025-07-01 03:01:24.970 + abcdefGhijkl
2025-07-01 03:01:24.994 """
2025-07-01 03:01:25.003
2025-07-01 03:01:25.014 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:25.026 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:25.038 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:25.054 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:25.066 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:25.078
2025-07-01 03:01:25.094 # search for the pair that matches best without being identical
2025-07-01 03:01:25.110 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:25.118 # on junk -- unless we have to)
2025-07-01 03:01:25.130 for j in range(blo, bhi):
2025-07-01 03:01:25.142 bj = b[j]
2025-07-01 03:01:25.162 cruncher.set_seq2(bj)
2025-07-01 03:01:25.182 for i in range(alo, ahi):
2025-07-01 03:01:25.198 ai = a[i]
2025-07-01 03:01:25.206 if ai == bj:
2025-07-01 03:01:25.216 if eqi is None:
2025-07-01 03:01:25.226 eqi, eqj = i, j
2025-07-01 03:01:25.238 continue
2025-07-01 03:01:25.246 cruncher.set_seq1(ai)
2025-07-01 03:01:25.258 # computing similarity is expensive, so use the quick
2025-07-01 03:01:25.270 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:25.286 # compares by a factor of 3.
2025-07-01 03:01:25.294 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:25.310 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:25.322 # of the computation is cached by cruncher
2025-07-01 03:01:25.337 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:25.354 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:25.362 cruncher.ratio() > best_ratio:
2025-07-01 03:01:25.374 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:25.390 if best_ratio < cutoff:
2025-07-01 03:01:25.398 # no non-identical "pretty close" pair
2025-07-01 03:01:25.406 if eqi is None:
2025-07-01 03:01:25.414 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:25.432 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:25.445 return
2025-07-01 03:01:25.453 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:25.465 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:25.473 else:
2025-07-01 03:01:25.482 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:25.494 eqi = None
2025-07-01 03:01:25.508
2025-07-01 03:01:25.518 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:25.536 # identical
2025-07-01 03:01:25.550
2025-07-01 03:01:25.570 # pump out diffs from before the synch point
2025-07-01 03:01:25.578 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:25.586
2025-07-01 03:01:25.602 # do intraline marking on the synch pair
2025-07-01 03:01:25.614 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:25.626 if eqi is None:
2025-07-01 03:01:25.638 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:25.650 atags = btags = ""
2025-07-01 03:01:25.664 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:25.678 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:25.690 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:25.700 if tag == 'replace':
2025-07-01 03:01:25.714 atags += '^' * la
2025-07-01 03:01:25.721 btags += '^' * lb
2025-07-01 03:01:25.730 elif tag == 'delete':
2025-07-01 03:01:25.742 atags += '-' * la
2025-07-01 03:01:25.750 elif tag == 'insert':
2025-07-01 03:01:25.766 btags += '+' * lb
2025-07-01 03:01:25.774 elif tag == 'equal':
2025-07-01 03:01:25.781 atags += ' ' * la
2025-07-01 03:01:25.794 btags += ' ' * lb
2025-07-01 03:01:25.802 else:
2025-07-01 03:01:25.818 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:25.834 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:25.846 else:
2025-07-01 03:01:25.854 # the synch pair is identical
2025-07-01 03:01:25.865 yield '  ' + aelt
2025-07-01 03:01:25.882
2025-07-01 03:01:25.890 # pump out diffs from after the synch point
2025-07-01 03:01:25.906 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:25.918
2025-07-01 03:01:25.926 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:25.938 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:25.950
2025-07-01 03:01:25.962 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:25.978 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:25.986 alo = 155, ahi = 1101
2025-07-01 03:01:25.998 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:26.010 blo = 155, bhi = 1101
2025-07-01 03:01:26.030
2025-07-01 03:01:26.038 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:26.046 g = []
2025-07-01 03:01:26.054 if alo < ahi:
2025-07-01 03:01:26.069 if blo < bhi:
2025-07-01 03:01:26.082 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:26.090 else:
2025-07-01 03:01:26.100 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:26.113 elif blo < bhi:
2025-07-01 03:01:26.122 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:26.138
2025-07-01 03:01:26.150 >       yield from g
2025-07-01 03:01:26.158
2025-07-01 03:01:26.170 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:26.182 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:26.190
2025-07-01 03:01:26.202 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:26.218 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:26.226 alo = 155, ahi = 1101
2025-07-01 03:01:26.238 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:26.250 blo = 155, bhi = 1101
2025-07-01 03:01:26.258
2025-07-01 03:01:26.274 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:26.282 r"""
2025-07-01 03:01:26.294 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:26.306 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:26.322 synch point, and intraline difference marking is done on the
2025-07-01 03:01:26.338 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:26.347
2025-07-01 03:01:26.362 Example:
2025-07-01 03:01:26.374
2025-07-01 03:01:26.390 >>> d = Differ()
2025-07-01 03:01:26.402 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:26.414 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:26.426 >>> print(''.join(results), end="")
2025-07-01 03:01:26.434 - abcDefghiJkl
2025-07-01 03:01:26.456 + abcdefGhijkl
2025-07-01 03:01:26.478 """
2025-07-01 03:01:26.492
2025-07-01 03:01:26.502 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:26.513 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:26.521 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:26.536 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:26.553 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:26.563
2025-07-01 03:01:26.575 # search for the pair that matches best without being identical
2025-07-01 03:01:26.587 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:26.600 # on junk -- unless we have to)
2025-07-01 03:01:26.610 for j in range(blo, bhi):
2025-07-01 03:01:26.627 bj = b[j]
2025-07-01 03:01:26.638 cruncher.set_seq2(bj)
2025-07-01 03:01:26.653 for i in range(alo, ahi):
2025-07-01 03:01:26.665 ai = a[i]
2025-07-01 03:01:26.669 if ai == bj:
2025-07-01 03:01:26.674 if eqi is None:
2025-07-01 03:01:26.678 eqi, eqj = i, j
2025-07-01 03:01:26.683 continue
2025-07-01 03:01:26.689 cruncher.set_seq1(ai)
2025-07-01 03:01:26.694 # computing similarity is expensive, so use the quick
2025-07-01 03:01:26.699 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:26.703 # compares by a factor of 3.
2025-07-01 03:01:26.708 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:26.712 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:26.716 # of the computation is cached by cruncher
2025-07-01 03:01:26.721 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:26.725 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:26.730 cruncher.ratio() > best_ratio:
2025-07-01 03:01:26.734 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:26.739 if best_ratio < cutoff:
2025-07-01 03:01:26.743 # no non-identical "pretty close" pair
2025-07-01 03:01:26.747 if eqi is None:
2025-07-01 03:01:26.752 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:26.757 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:26.761 return
2025-07-01 03:01:26.766 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:26.770 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:26.774 else:
2025-07-01 03:01:26.779 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:26.783 eqi = None
2025-07-01 03:01:26.787
2025-07-01 03:01:26.792 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:26.796 # identical
2025-07-01 03:01:26.800
2025-07-01 03:01:26.805 # pump out diffs from before the synch point
2025-07-01 03:01:26.809 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:26.813
2025-07-01 03:01:26.818 # do intraline marking on the synch pair
2025-07-01 03:01:26.822 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:26.827 if eqi is None:
2025-07-01 03:01:26.831 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:26.835 atags = btags = ""
2025-07-01 03:01:26.840 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:26.844 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:26.849 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:26.853 if tag == 'replace':
2025-07-01 03:01:26.858 atags += '^' * la
2025-07-01 03:01:26.863 btags += '^' * lb
2025-07-01 03:01:26.867 elif tag == 'delete':
2025-07-01 03:01:26.873 atags += '-' * la
2025-07-01 03:01:26.878 elif tag == 'insert':
2025-07-01 03:01:26.882 btags += '+' * lb
2025-07-01 03:01:26.887 elif tag == 'equal':
2025-07-01 03:01:26.891 atags += ' ' * la
2025-07-01 03:01:26.896 btags += ' ' * lb
2025-07-01 03:01:26.900 else:
2025-07-01 03:01:26.905 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:26.909 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:26.914 else:
2025-07-01 03:01:26.918 # the synch pair is identical
2025-07-01 03:01:26.922 yield '  ' + aelt
2025-07-01 03:01:26.927
2025-07-01 03:01:26.931 # pump out diffs from after the synch point
2025-07-01 03:01:26.936 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:26.940
2025-07-01 03:01:26.944 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:26.949 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:26.953
2025-07-01 03:01:26.958 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:26.963 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:26.967 alo = 158, ahi = 1101
2025-07-01 03:01:26.972 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:26.977 blo = 158, bhi = 1101
2025-07-01 03:01:26.981
2025-07-01 03:01:26.985 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:26.990 g = []
2025-07-01 03:01:26.994 if alo < ahi:
2025-07-01 03:01:26.998 if blo < bhi:
2025-07-01 03:01:27.003 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:27.007 else:
2025-07-01 03:01:27.012 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:27.016 elif blo < bhi:
2025-07-01 03:01:27.021 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:27.025
2025-07-01 03:01:27.029 >       yield from g
2025-07-01 03:01:27.033
2025-07-01 03:01:27.038 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:27.042 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:27.047
2025-07-01 03:01:27.051 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:27.056 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:27.060 alo = 158, ahi = 1101
2025-07-01 03:01:27.065 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:27.070 blo = 158, bhi = 1101
2025-07-01 03:01:27.074
2025-07-01 03:01:27.079 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:27.083 r"""
2025-07-01 03:01:27.088 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:27.092 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:27.100 synch point, and intraline difference marking is done on the
2025-07-01 03:01:27.107 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:27.111
2025-07-01 03:01:27.115 Example:
2025-07-01 03:01:27.120
2025-07-01 03:01:27.124 >>> d = Differ()
2025-07-01 03:01:27.128 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:27.133 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:27.138 >>> print(''.join(results), end="")
2025-07-01 03:01:27.143 - abcDefghiJkl
2025-07-01 03:01:27.152 + abcdefGhijkl
2025-07-01 03:01:27.162 """
2025-07-01 03:01:27.167
2025-07-01 03:01:27.172 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:27.176 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:27.182 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:27.187 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:27.193 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:27.198
2025-07-01 03:01:27.203 # search for the pair that matches best without being identical
2025-07-01 03:01:27.208 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:27.214 # on junk -- unless we have to)
2025-07-01 03:01:27.219 for j in range(blo, bhi):
2025-07-01 03:01:27.224 bj = b[j]
2025-07-01 03:01:27.230 cruncher.set_seq2(bj)
2025-07-01 03:01:27.235 for i in range(alo, ahi):
2025-07-01 03:01:27.241 ai = a[i]
2025-07-01 03:01:27.245 if ai == bj:
2025-07-01 03:01:27.250 if eqi is None:
2025-07-01 03:01:27.254 eqi, eqj = i, j
2025-07-01 03:01:27.258 continue
2025-07-01 03:01:27.263 cruncher.set_seq1(ai)
2025-07-01 03:01:27.268 # computing similarity is expensive, so use the quick
2025-07-01 03:01:27.274 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:27.279 # compares by a factor of 3.
2025-07-01 03:01:27.284 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:27.289 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:27.294 # of the computation is cached by cruncher
2025-07-01 03:01:27.299 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:27.305 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:27.310 cruncher.ratio() > best_ratio:
2025-07-01 03:01:27.315 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:27.321 if best_ratio < cutoff:
2025-07-01 03:01:27.327 # no non-identical "pretty close" pair
2025-07-01 03:01:27.332 if eqi is None:
2025-07-01 03:01:27.337 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:27.341 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:27.347 return
2025-07-01 03:01:27.352 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:27.357 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:27.362 else:
2025-07-01 03:01:27.367 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:27.373 eqi = None
2025-07-01 03:01:27.378
2025-07-01 03:01:27.384 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:27.389 # identical
2025-07-01 03:01:27.395
2025-07-01 03:01:27.404 # pump out diffs from before the synch point
2025-07-01 03:01:27.416 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:27.430
2025-07-01 03:01:27.438 # do intraline marking on the synch pair
2025-07-01 03:01:27.450 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:27.462 if eqi is None:
2025-07-01 03:01:27.478 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:27.486 atags = btags = ""
2025-07-01 03:01:27.502 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:27.518 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:27.526 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:27.542 if tag == 'replace':
2025-07-01 03:01:27.550 atags += '^' * la
2025-07-01 03:01:27.562 btags += '^' * lb
2025-07-01 03:01:27.574 elif tag == 'delete':
2025-07-01 03:01:27.584 atags += '-' * la
2025-07-01 03:01:27.598 elif tag == 'insert':
2025-07-01 03:01:27.610 btags += '+' * lb
2025-07-01 03:01:27.622 elif tag == 'equal':
2025-07-01 03:01:27.630 atags += ' ' * la
2025-07-01 03:01:27.642 btags += ' ' * lb
2025-07-01 03:01:27.654 else:
2025-07-01 03:01:27.662 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:27.674 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:27.684 else:
2025-07-01 03:01:27.698 # the synch pair is identical
2025-07-01 03:01:27.706 yield '  ' + aelt
2025-07-01 03:01:27.719
2025-07-01 03:01:27.727 # pump out diffs from after the synch point
2025-07-01 03:01:27.738 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:27.749
2025-07-01 03:01:27.758 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:27.769 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:27.781
2025-07-01 03:01:27.796 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:27.807 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:27.818 alo = 159, ahi = 1101
2025-07-01 03:01:27.830 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:27.842 blo = 159, bhi = 1101
2025-07-01 03:01:27.854
2025-07-01 03:01:27.862 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:27.874 g = []
2025-07-01 03:01:27.882 if alo < ahi:
2025-07-01 03:01:27.894 if blo < bhi:
2025-07-01 03:01:27.902 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:27.910 else:
2025-07-01 03:01:27.922 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:27.929 elif blo < bhi:
2025-07-01 03:01:27.946 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:27.953
2025-07-01 03:01:27.965 >       yield from g
2025-07-01 03:01:27.974
2025-07-01 03:01:27.990 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:27.999 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:28.007
2025-07-01 03:01:28.023 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:28.042 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:28.054 alo = 159, ahi = 1101
2025-07-01 03:01:28.066 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:28.074 blo = 159, bhi = 1101
2025-07-01 03:01:28.090
2025-07-01 03:01:28.110 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:28.122 r"""
2025-07-01 03:01:28.130 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:28.142 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:28.154 synch point, and intraline difference marking is done on the
2025-07-01 03:01:28.162 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:28.174
2025-07-01 03:01:28.182 Example:
2025-07-01 03:01:28.190
2025-07-01 03:01:28.206 >>> d = Differ()
2025-07-01 03:01:28.214 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:28.230 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:28.242 >>> print(''.join(results), end="")
2025-07-01 03:01:28.254 - abcDefghiJkl
2025-07-01 03:01:28.274 + abcdefGhijkl
2025-07-01 03:01:28.294 """
2025-07-01 03:01:28.302
2025-07-01 03:01:28.314 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:28.322 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:28.338 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:28.346 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:28.362 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:28.370
2025-07-01 03:01:28.382 # search for the pair that matches best without being identical
2025-07-01 03:01:28.394 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:28.402 # on junk -- unless we have to)
2025-07-01 03:01:28.410 for j in range(blo, bhi):
2025-07-01 03:01:28.422 bj = b[j]
2025-07-01 03:01:28.438 cruncher.set_seq2(bj)
2025-07-01 03:01:28.450 for i in range(alo, ahi):
2025-07-01 03:01:28.460 ai = a[i]
2025-07-01 03:01:28.474 if ai == bj:
2025-07-01 03:01:28.490 if eqi is None:
2025-07-01 03:01:28.498 eqi, eqj = i, j
2025-07-01 03:01:28.509 continue
2025-07-01 03:01:28.527 cruncher.set_seq1(ai)
2025-07-01 03:01:28.540 # computing similarity is expensive, so use the quick
2025-07-01 03:01:28.551 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:28.563 # compares by a factor of 3.
2025-07-01 03:01:28.573 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:28.586 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:28.597 # of the computation is cached by cruncher
2025-07-01 03:01:28.610 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:28.626 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:28.642 cruncher.ratio() > best_ratio:
2025-07-01 03:01:28.658 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:28.670 if best_ratio < cutoff:
2025-07-01 03:01:28.678 # no non-identical "pretty close" pair
2025-07-01 03:01:28.690 if eqi is None:
2025-07-01 03:01:28.702 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:28.714 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:28.722 return
2025-07-01 03:01:28.734 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:28.746 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:28.754 else:
2025-07-01 03:01:28.770 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:28.778 eqi = None
2025-07-01 03:01:28.786
2025-07-01 03:01:28.798 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:28.810 # identical
2025-07-01 03:01:28.818
2025-07-01 03:01:28.830 # pump out diffs from before the synch point
2025-07-01 03:01:28.838 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:28.854
2025-07-01 03:01:28.866 # do intraline marking on the synch pair
2025-07-01 03:01:28.874 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:28.885 if eqi is None:
2025-07-01 03:01:28.897 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:28.903 atags = btags = ""
2025-07-01 03:01:28.909 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:28.915 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:28.921 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:28.926 if tag == 'replace':
2025-07-01 03:01:28.932 atags += '^' * la
2025-07-01 03:01:28.938 btags += '^' * lb
2025-07-01 03:01:28.943 elif tag == 'delete':
2025-07-01 03:01:28.949 atags += '-' * la
2025-07-01 03:01:28.955 elif tag == 'insert':
2025-07-01 03:01:28.961 btags += '+' * lb
2025-07-01 03:01:28.967 elif tag == 'equal':
2025-07-01 03:01:28.972 atags += ' ' * la
2025-07-01 03:01:28.978 btags += ' ' * lb
2025-07-01 03:01:28.985 else:
2025-07-01 03:01:28.990 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:28.998 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:29.005 else:
2025-07-01 03:01:29.010 # the synch pair is identical
2025-07-01 03:01:29.015 yield '  ' + aelt
2025-07-01 03:01:29.028
2025-07-01 03:01:29.046 # pump out diffs from after the synch point
2025-07-01 03:01:29.054 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:29.062
2025-07-01 03:01:29.074 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:29.086 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:29.098
2025-07-01 03:01:29.110 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:29.118 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:29.130 alo = 160, ahi = 1101
2025-07-01 03:01:29.142 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:29.150 blo = 160, bhi = 1101
2025-07-01 03:01:29.162
2025-07-01 03:01:29.174 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:29.182 g = []
2025-07-01 03:01:29.198 if alo < ahi:
2025-07-01 03:01:29.206 if blo < bhi:
2025-07-01 03:01:29.220 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:29.234 else:
2025-07-01 03:01:29.246 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:29.258 elif blo < bhi:
2025-07-01 03:01:29.266 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:29.278
2025-07-01 03:01:29.286 >       yield from g
2025-07-01 03:01:29.294
2025-07-01 03:01:29.306 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:29.314 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:29.325
2025-07-01 03:01:29.336 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:29.354 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:29.362 alo = 160, ahi = 1101
2025-07-01 03:01:29.374 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:29.382 blo = 160, bhi = 1101
2025-07-01 03:01:29.394
2025-07-01 03:01:29.402 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:29.414 r"""
2025-07-01 03:01:29.422 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:29.438 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:29.454 synch point, and intraline difference marking is done on the
2025-07-01 03:01:29.462 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:29.470
2025-07-01 03:01:29.478 Example:
2025-07-01 03:01:29.492
2025-07-01 03:01:29.498 >>> d = Differ()
2025-07-01 03:01:29.508 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:29.518 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:29.530 >>> print(''.join(results), end="")
2025-07-01 03:01:29.542 - abcDefghiJkl
2025-07-01 03:01:29.561 + abcdefGhijkl
2025-07-01 03:01:29.585 """
2025-07-01 03:01:29.598
2025-07-01 03:01:29.606 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:29.620 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:29.630 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:29.646 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:29.654 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:29.668
2025-07-01 03:01:29.678 # search for the pair that matches best without being identical
2025-07-01 03:01:29.692 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:29.704 # on junk -- unless we have to)
2025-07-01 03:01:29.714 for j in range(blo, bhi):
2025-07-01 03:01:29.722 bj = b[j]
2025-07-01 03:01:29.738 cruncher.set_seq2(bj)
2025-07-01 03:01:29.747 for i in range(alo, ahi):
2025-07-01 03:01:29.758 ai = a[i]
2025-07-01 03:01:29.774 if ai == bj:
2025-07-01 03:01:29.782 if eqi is None:
2025-07-01 03:01:29.792 eqi, eqj = i, j
2025-07-01 03:01:29.805 continue
2025-07-01 03:01:29.817 cruncher.set_seq1(ai)
2025-07-01 03:01:29.836 # computing similarity is expensive, so use the quick
2025-07-01 03:01:29.843 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:29.855 # compares by a factor of 3.
2025-07-01 03:01:29.866 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:29.874 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:29.886 # of the computation is cached by cruncher
2025-07-01 03:01:29.902 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:29.910 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:29.918 cruncher.ratio() > best_ratio:
2025-07-01 03:01:29.930 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:29.937 if best_ratio < cutoff:
2025-07-01 03:01:29.949 # no non-identical "pretty close" pair
2025-07-01 03:01:29.966 if eqi is None:
2025-07-01 03:01:29.982 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:29.993 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:30.011 return
2025-07-01 03:01:30.022 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:30.034 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:30.046 else:
2025-07-01 03:01:30.058 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:30.070 eqi = None
2025-07-01 03:01:30.082
2025-07-01 03:01:30.090 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:30.098 # identical
2025-07-01 03:01:30.110
2025-07-01 03:01:30.120 # pump out diffs from before the synch point
2025-07-01 03:01:30.133 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:30.142
2025-07-01 03:01:30.154 # do intraline marking on the synch pair
2025-07-01 03:01:30.166 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:30.182 if eqi is None:
2025-07-01 03:01:30.190 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:30.204 atags = btags = ""
2025-07-01 03:01:30.218 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:30.230 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:30.246 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:30.254 if tag == 'replace':
2025-07-01 03:01:30.262 atags += '^' * la
2025-07-01 03:01:30.278 btags += '^' * lb
2025-07-01 03:01:30.290 elif tag == 'delete':
2025-07-01 03:01:30.298 atags += '-' * la
2025-07-01 03:01:30.312 elif tag == 'insert':
2025-07-01 03:01:30.322 btags += '+' * lb
2025-07-01 03:01:30.334 elif tag == 'equal':
2025-07-01 03:01:30.341 atags += ' ' * la
2025-07-01 03:01:30.353 btags += ' ' * lb
2025-07-01 03:01:30.361 else:
2025-07-01 03:01:30.374 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:30.381 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:30.394 else:
2025-07-01 03:01:30.402 # the synch pair is identical
2025-07-01 03:01:30.414 yield '  ' + aelt
2025-07-01 03:01:30.421
2025-07-01 03:01:30.434 # pump out diffs from after the synch point
2025-07-01 03:01:30.442 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:30.454
2025-07-01 03:01:30.466 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:30.477 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:30.493
2025-07-01 03:01:30.506 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:30.514 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:30.526 alo = 161, ahi = 1101
2025-07-01 03:01:30.536 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:30.550 blo = 161, bhi = 1101
2025-07-01 03:01:30.562
2025-07-01 03:01:30.574 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:30.586 g = []
2025-07-01 03:01:30.598 if alo < ahi:
2025-07-01 03:01:30.605 if blo < bhi:
2025-07-01 03:01:30.618 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:30.626 else:
2025-07-01 03:01:30.640 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:30.649 elif blo < bhi:
2025-07-01 03:01:30.658 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:30.672
2025-07-01 03:01:30.680 >       yield from g
2025-07-01 03:01:30.696
2025-07-01 03:01:30.706 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:30.714 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:30.726
2025-07-01 03:01:30.734 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:30.746 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:30.758 alo = 161, ahi = 1101
2025-07-01 03:01:30.766 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:30.778 blo = 161, bhi = 1101
2025-07-01 03:01:30.786
2025-07-01 03:01:30.798 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:30.806 r"""
2025-07-01 03:01:30.818 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:30.830 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:30.842 synch point, and intraline difference marking is done on the
2025-07-01 03:01:30.850 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:30.858
2025-07-01 03:01:30.874 Example:
2025-07-01 03:01:30.882
2025-07-01 03:01:30.898 >>> d = Differ()
2025-07-01 03:01:30.906 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:30.918 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:30.926 >>> print(''.join(results), end="")
2025-07-01 03:01:30.941 - abcDefghiJkl
2025-07-01 03:01:30.965 + abcdefGhijkl
2025-07-01 03:01:30.990 """
2025-07-01 03:01:30.998
2025-07-01 03:01:31.011 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:31.026 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:31.033 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:31.042 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:31.054 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:31.062
2025-07-01 03:01:31.074 # search for the pair that matches best without being identical
2025-07-01 03:01:31.082 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:31.094 # on junk -- unless we have to)
2025-07-01 03:01:31.102 for j in range(blo, bhi):
2025-07-01 03:01:31.114 bj = b[j]
2025-07-01 03:01:31.122 cruncher.set_seq2(bj)
2025-07-01 03:01:31.132 for i in range(alo, ahi):
2025-07-01 03:01:31.146 ai = a[i]
2025-07-01 03:01:31.158 if ai == bj:
2025-07-01 03:01:31.166 if eqi is None:
2025-07-01 03:01:31.178 eqi, eqj = i, j
2025-07-01 03:01:31.186 continue
2025-07-01 03:01:31.198 cruncher.set_seq1(ai)
2025-07-01 03:01:31.210 # computing similarity is expensive, so use the quick
2025-07-01 03:01:31.222 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:31.230 # compares by a factor of 3.
2025-07-01 03:01:31.246 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:31.253 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:31.266 # of the computation is cached by cruncher
2025-07-01 03:01:31.278 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:31.294 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:31.302 cruncher.ratio() > best_ratio:
2025-07-01 03:01:31.317 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:31.330 if best_ratio < cutoff:
2025-07-01 03:01:31.338 # no non-identical "pretty close" pair
2025-07-01 03:01:31.348 if eqi is None:
2025-07-01 03:01:31.366 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:31.378 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:31.390 return
2025-07-01 03:01:31.398 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:31.414 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:31.430 else:
2025-07-01 03:01:31.442 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:31.454 eqi = None
2025-07-01 03:01:31.461
2025-07-01 03:01:31.474 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:31.486 # identical
2025-07-01 03:01:31.502
2025-07-01 03:01:31.509 # pump out diffs from before the synch point
2025-07-01 03:01:31.526 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:31.534
2025-07-01 03:01:31.550 # do intraline marking on the synch pair
2025-07-01 03:01:31.558 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:31.570 if eqi is None:
2025-07-01 03:01:31.586 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:31.594 atags = btags = ""
2025-07-01 03:01:31.606 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:31.614 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:31.630 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:31.638 if tag == 'replace':
2025-07-01 03:01:31.646 atags += '^' * la
2025-07-01 03:01:31.662 btags += '^' * lb
2025-07-01 03:01:31.673 elif tag == 'delete':
2025-07-01 03:01:31.686 atags += '-' * la
2025-07-01 03:01:31.698 elif tag == 'insert':
2025-07-01 03:01:31.710 btags += '+' * lb
2025-07-01 03:01:31.718 elif tag == 'equal':
2025-07-01 03:01:31.730 atags += ' ' * la
2025-07-01 03:01:31.738 btags += ' ' * lb
2025-07-01 03:01:31.750 else:
2025-07-01 03:01:31.766 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:31.774 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:31.782 else:
2025-07-01 03:01:31.794 # the synch pair is identical
2025-07-01 03:01:31.806 yield '  ' + aelt
2025-07-01 03:01:31.814
2025-07-01 03:01:31.822 # pump out diffs from after the synch point
2025-07-01 03:01:31.834 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:31.846
2025-07-01 03:01:31.854 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:31.866 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:31.874
2025-07-01 03:01:31.886 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:31.902 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:31.910 alo = 162, ahi = 1101
2025-07-01 03:01:31.918 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:31.930 blo = 162, bhi = 1101
2025-07-01 03:01:31.938
2025-07-01 03:01:31.950 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:31.962 g = []
2025-07-01 03:01:31.970 if alo < ahi:
2025-07-01 03:01:31.982 if blo < bhi:
2025-07-01 03:01:31.990 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:32.002 else:
2025-07-01 03:01:32.010 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:32.022 elif blo < bhi:
2025-07-01 03:01:32.032 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:32.046
2025-07-01 03:01:32.054 >       yield from g
2025-07-01 03:01:32.066
2025-07-01 03:01:32.074 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:32.090 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:32.110
2025-07-01 03:01:32.118 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:32.128 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:32.136 alo = 162, ahi = 1101
2025-07-01 03:01:32.150 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:32.162 blo = 162, bhi = 1101
2025-07-01 03:01:32.170
2025-07-01 03:01:32.182 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:32.194 r"""
2025-07-01 03:01:32.206 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:32.213 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:32.226 synch point, and intraline difference marking is done on the
2025-07-01 03:01:32.238 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:32.247
2025-07-01 03:01:32.266 Example:
2025-07-01 03:01:32.274
2025-07-01 03:01:32.282 >>> d = Differ()
2025-07-01 03:01:32.290 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:32.302 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:32.314 >>> print(''.join(results), end="")
2025-07-01 03:01:32.322 - abcDefghiJkl
2025-07-01 03:01:32.342 + abcdefGhijkl
2025-07-01 03:01:32.362 """
2025-07-01 03:01:32.374
2025-07-01 03:01:32.382 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:32.394 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:32.406 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:32.418 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:32.426 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:32.438
2025-07-01 03:01:32.447 # search for the pair that matches best without being identical
2025-07-01 03:01:32.462 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:32.470 # on junk -- unless we have to)
2025-07-01 03:01:32.481 for j in range(blo, bhi):
2025-07-01 03:01:32.497 bj = b[j]
2025-07-01 03:01:32.510 cruncher.set_seq2(bj)
2025-07-01 03:01:32.522 for i in range(alo, ahi):
2025-07-01 03:01:32.538 ai = a[i]
2025-07-01 03:01:32.554 if ai == bj:
2025-07-01 03:01:32.566 if eqi is None:
2025-07-01 03:01:32.582 eqi, eqj = i, j
2025-07-01 03:01:32.590 continue
2025-07-01 03:01:32.602 cruncher.set_seq1(ai)
2025-07-01 03:01:32.610 # computing similarity is expensive, so use the quick
2025-07-01 03:01:32.626 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:32.638 # compares by a factor of 3.
2025-07-01 03:01:32.654 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:32.662 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:32.678 # of the computation is cached by cruncher
2025-07-01 03:01:32.690 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:32.702 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:32.710 cruncher.ratio() > best_ratio:
2025-07-01 03:01:32.720 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:32.734 if best_ratio < cutoff:
2025-07-01 03:01:32.746 # no non-identical "pretty close" pair
2025-07-01 03:01:32.758 if eqi is None:
2025-07-01 03:01:32.770 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:32.782 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:32.794 return
2025-07-01 03:01:32.810 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:32.818 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:32.830 else:
2025-07-01 03:01:32.846 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:32.854 eqi = None
2025-07-01 03:01:32.866
2025-07-01 03:01:32.878 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:32.890 # identical
2025-07-01 03:01:32.906
2025-07-01 03:01:32.917 # pump out diffs from before the synch point
2025-07-01 03:01:32.930 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:32.947
2025-07-01 03:01:32.954 # do intraline marking on the synch pair
2025-07-01 03:01:32.962 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:32.974 if eqi is None:
2025-07-01 03:01:32.990 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:33.001 atags = btags = ""
2025-07-01 03:01:33.018 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:33.026 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:33.038 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:33.050 if tag == 'replace':
2025-07-01 03:01:33.062 atags += '^' * la
2025-07-01 03:01:33.070 btags += '^' * lb
2025-07-01 03:01:33.082 elif tag == 'delete':
2025-07-01 03:01:33.094 atags += '-' * la
2025-07-01 03:01:33.102 elif tag == 'insert':
2025-07-01 03:01:33.114 btags += '+' * lb
2025-07-01 03:01:33.130 elif tag == 'equal':
2025-07-01 03:01:33.138 atags += ' ' * la
2025-07-01 03:01:33.150 btags += ' ' * lb
2025-07-01 03:01:33.158 else:
2025-07-01 03:01:33.163 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:33.167 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:33.172 else:
2025-07-01 03:01:33.177 # the synch pair is identical
2025-07-01 03:01:33.182 yield '  ' + aelt
2025-07-01 03:01:33.187
2025-07-01 03:01:33.192 # pump out diffs from after the synch point
2025-07-01 03:01:33.196 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:33.201
2025-07-01 03:01:33.206 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:33.210 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:33.224
2025-07-01 03:01:33.231 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:33.236 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:33.241 alo = 163, ahi = 1101
2025-07-01 03:01:33.246 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:33.251 blo = 163, bhi = 1101
2025-07-01 03:01:33.255
2025-07-01 03:01:33.260 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:33.265 g = []
2025-07-01 03:01:33.270 if alo < ahi:
2025-07-01 03:01:33.274 if blo < bhi:
2025-07-01 03:01:33.279 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:33.284 else:
2025-07-01 03:01:33.288 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:33.293 elif blo < bhi:
2025-07-01 03:01:33.297 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:33.302
2025-07-01 03:01:33.306 >       yield from g
2025-07-01 03:01:33.311
2025-07-01 03:01:33.316 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:33.320 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:33.325
2025-07-01 03:01:33.330 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:33.336 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:33.340 alo = 163, ahi = 1101
2025-07-01 03:01:33.346 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:33.351 blo = 163, bhi = 1101
2025-07-01 03:01:33.355
2025-07-01 03:01:33.360 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:33.364 r"""
2025-07-01 03:01:33.369 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:33.374 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:33.379 synch point, and intraline difference marking is done on the
2025-07-01 03:01:33.385 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:33.390
2025-07-01 03:01:33.395 Example:
2025-07-01 03:01:33.400
2025-07-01 03:01:33.404 >>> d = Differ()
2025-07-01 03:01:33.409 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:33.415 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:33.420 >>> print(''.join(results), end="")
2025-07-01 03:01:33.425 - abcDefghiJkl
2025-07-01 03:01:33.434 + abcdefGhijkl
2025-07-01 03:01:33.444 """
2025-07-01 03:01:33.448
2025-07-01 03:01:33.453 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:33.458 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:33.462 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:33.467 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:33.471 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:33.476
2025-07-01 03:01:33.480 # search for the pair that matches best without being identical
2025-07-01 03:01:33.485 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:33.490 # on junk -- unless we have to)
2025-07-01 03:01:33.494 for j in range(blo, bhi):
2025-07-01 03:01:33.499 bj = b[j]
2025-07-01 03:01:33.503 cruncher.set_seq2(bj)
2025-07-01 03:01:33.508 for i in range(alo, ahi):
2025-07-01 03:01:33.512 ai = a[i]
2025-07-01 03:01:33.517 if ai == bj:
2025-07-01 03:01:33.521 if eqi is None:
2025-07-01 03:01:33.526 eqi, eqj = i, j
2025-07-01 03:01:33.530 continue
2025-07-01 03:01:33.535 cruncher.set_seq1(ai)
2025-07-01 03:01:33.541 # computing similarity is expensive, so use the quick
2025-07-01 03:01:33.546 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:33.553 # compares by a factor of 3.
2025-07-01 03:01:33.558 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:33.562 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:33.567 # of the computation is cached by cruncher
2025-07-01 03:01:33.572 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:33.576 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:33.581 cruncher.ratio() > best_ratio:
2025-07-01 03:01:33.586 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:33.591 if best_ratio < cutoff:
2025-07-01 03:01:33.596 # no non-identical "pretty close" pair
2025-07-01 03:01:33.601 if eqi is None:
2025-07-01 03:01:33.605 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:33.610 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:33.614 return
2025-07-01 03:01:33.619 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:33.623 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:33.628 else:
2025-07-01 03:01:33.632 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:33.636 eqi = None
2025-07-01 03:01:33.641
2025-07-01 03:01:33.646 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:33.650 # identical
2025-07-01 03:01:33.655
2025-07-01 03:01:33.659 # pump out diffs from before the synch point
2025-07-01 03:01:33.664 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:33.668
2025-07-01 03:01:33.673 # do intraline marking on the synch pair
2025-07-01 03:01:33.678 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:33.682 if eqi is None:
2025-07-01 03:01:33.687 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:33.692 atags = btags = ""
2025-07-01 03:01:33.696 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:33.701 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:33.705 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:33.710 if tag == 'replace':
2025-07-01 03:01:33.714 atags += '^' * la
2025-07-01 03:01:33.719 btags += '^' * lb
2025-07-01 03:01:33.723 elif tag == 'delete':
2025-07-01 03:01:33.728 atags += '-' * la
2025-07-01 03:01:33.732 elif tag == 'insert':
2025-07-01 03:01:33.737 btags += '+' * lb
2025-07-01 03:01:33.741 elif tag == 'equal':
2025-07-01 03:01:33.746 atags += ' ' * la
2025-07-01 03:01:33.751 btags += ' ' * lb
2025-07-01 03:01:33.755 else:
2025-07-01 03:01:33.759 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:33.764 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:33.770 else:
2025-07-01 03:01:33.776 # the synch pair is identical
2025-07-01 03:01:33.781 yield '  ' + aelt
2025-07-01 03:01:33.785
2025-07-01 03:01:33.789 # pump out diffs from after the synch point
2025-07-01 03:01:33.794 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:33.798
2025-07-01 03:01:33.803 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:33.807 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:33.811
2025-07-01 03:01:33.816 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:33.821 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:33.825 alo = 164, ahi = 1101
2025-07-01 03:01:33.830 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:33.834 blo = 164, bhi = 1101
2025-07-01 03:01:33.839
2025-07-01 03:01:33.843 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:33.848 g = []
2025-07-01 03:01:33.854 if alo < ahi:
2025-07-01 03:01:33.859 if blo < bhi:
2025-07-01 03:01:33.863 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:33.868 else:
2025-07-01 03:01:33.872 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:33.877 elif blo < bhi:
2025-07-01 03:01:33.881 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:33.886
2025-07-01 03:01:33.890 >       yield from g
2025-07-01 03:01:33.895
2025-07-01 03:01:33.899 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:33.904 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:33.910
2025-07-01 03:01:33.917 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:33.924 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:33.931 alo = 164, ahi = 1101
2025-07-01 03:01:33.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:33.944 blo = 164, bhi = 1101
2025-07-01 03:01:33.950
2025-07-01 03:01:33.957 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:33.963 r"""
2025-07-01 03:01:33.969 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:33.975 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:33.981 synch point, and intraline difference marking is done on the
2025-07-01 03:01:33.987 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:33.993
2025-07-01 03:01:33.999 Example:
2025-07-01 03:01:34.005
2025-07-01 03:01:34.009 >>> d = Differ()
2025-07-01 03:01:34.014 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:34.018 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:34.023 >>> print(''.join(results), end="")
2025-07-01 03:01:34.027 - abcDefghiJkl
2025-07-01 03:01:34.036 + abcdefGhijkl
2025-07-01 03:01:34.044 """
2025-07-01 03:01:34.049
2025-07-01 03:01:34.053 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:34.058 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:34.062 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:34.067 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:34.071 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:34.076
2025-07-01 03:01:34.080 # search for the pair that matches best without being identical
2025-07-01 03:01:34.085 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:34.090 # on junk -- unless we have to)
2025-07-01 03:01:34.094 for j in range(blo, bhi):
2025-07-01 03:01:34.099 bj = b[j]
2025-07-01 03:01:34.103 cruncher.set_seq2(bj)
2025-07-01 03:01:34.107 for i in range(alo, ahi):
2025-07-01 03:01:34.112 ai = a[i]
2025-07-01 03:01:34.117 if ai == bj:
2025-07-01 03:01:34.121 if eqi is None:
2025-07-01 03:01:34.128 eqi, eqj = i, j
2025-07-01 03:01:34.133 continue
2025-07-01 03:01:34.137 cruncher.set_seq1(ai)
2025-07-01 03:01:34.142 # computing similarity is expensive, so use the quick
2025-07-01 03:01:34.147 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:34.151 # compares by a factor of 3.
2025-07-01 03:01:34.157 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:34.162 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:34.167 # of the computation is cached by cruncher
2025-07-01 03:01:34.172 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:34.177 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:34.183 cruncher.ratio() > best_ratio:
2025-07-01 03:01:34.188 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:34.194 if best_ratio < cutoff:
2025-07-01 03:01:34.199 # no non-identical "pretty close" pair
2025-07-01 03:01:34.204 if eqi is None:
2025-07-01 03:01:34.209 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:34.213 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:34.218 return
2025-07-01 03:01:34.222 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:34.227 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:34.231 else:
2025-07-01 03:01:34.236 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:34.240 eqi = None
2025-07-01 03:01:34.245
2025-07-01 03:01:34.250 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:34.254 # identical
2025-07-01 03:01:34.258
2025-07-01 03:01:34.263 # pump out diffs from before the synch point
2025-07-01 03:01:34.268 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:34.272
2025-07-01 03:01:34.277 # do intraline marking on the synch pair
2025-07-01 03:01:34.282 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:34.286 if eqi is None:
2025-07-01 03:01:34.293 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:34.298 atags = btags = ""
2025-07-01 03:01:34.303 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:34.307 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:34.312 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:34.317 if tag == 'replace':
2025-07-01 03:01:34.321 atags += '^' * la
2025-07-01 03:01:34.326 btags += '^' * lb
2025-07-01 03:01:34.330 elif tag == 'delete':
2025-07-01 03:01:34.335 atags += '-' * la
2025-07-01 03:01:34.339 elif tag == 'insert':
2025-07-01 03:01:34.344 btags += '+' * lb
2025-07-01 03:01:34.349 elif tag == 'equal':
2025-07-01 03:01:34.353 atags += ' ' * la
2025-07-01 03:01:34.358 btags += ' ' * lb
2025-07-01 03:01:34.362 else:
2025-07-01 03:01:34.372 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:34.386 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:34.397 else:
2025-07-01 03:01:34.414 # the synch pair is identical
2025-07-01 03:01:34.426 yield '  ' + aelt
2025-07-01 03:01:34.438
2025-07-01 03:01:34.446 # pump out diffs from after the synch point
2025-07-01 03:01:34.458 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:34.470
2025-07-01 03:01:34.486 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:34.494 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:34.506
2025-07-01 03:01:34.514 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:34.526 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:34.538 alo = 165, ahi = 1101
2025-07-01 03:01:34.557 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:34.570 blo = 165, bhi = 1101
2025-07-01 03:01:34.578
2025-07-01 03:01:34.590 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:34.606 g = []
2025-07-01 03:01:34.614 if alo < ahi:
2025-07-01 03:01:34.626 if blo < bhi:
2025-07-01 03:01:34.634 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:34.646 else:
2025-07-01 03:01:34.658 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:34.670 elif blo < bhi:
2025-07-01 03:01:34.686 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:34.694
2025-07-01 03:01:34.710 >       yield from g
2025-07-01 03:01:34.722
2025-07-01 03:01:34.730 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:34.744 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:34.758
2025-07-01 03:01:34.766 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:34.785 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:34.794 alo = 165, ahi = 1101
2025-07-01 03:01:34.806 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:34.822 blo = 165, bhi = 1101
2025-07-01 03:01:34.834
2025-07-01 03:01:34.842 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:34.850 r"""
2025-07-01 03:01:34.858 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:34.870 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:34.882 synch point, and intraline difference marking is done on the
2025-07-01 03:01:34.890 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:34.900
2025-07-01 03:01:34.910 Example:
2025-07-01 03:01:34.922
2025-07-01 03:01:34.934 >>> d = Differ()
2025-07-01 03:01:34.946 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:34.958 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:34.970 >>> print(''.join(results), end="")
2025-07-01 03:01:34.982 - abcDefghiJkl
2025-07-01 03:01:35.010 + abcdefGhijkl
2025-07-01 03:01:35.030 """
2025-07-01 03:01:35.042
2025-07-01 03:01:35.054 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:35.062 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:35.074 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:35.082 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:35.094 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:35.110
2025-07-01 03:01:35.122 # search for the pair that matches best without being identical
2025-07-01 03:01:35.134 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:35.146 # on junk -- unless we have to)
2025-07-01 03:01:35.154 for j in range(blo, bhi):
2025-07-01 03:01:35.166 bj = b[j]
2025-07-01 03:01:35.178 cruncher.set_seq2(bj)
2025-07-01 03:01:35.188 for i in range(alo, ahi):
2025-07-01 03:01:35.205 ai = a[i]
2025-07-01 03:01:35.218 if ai == bj:
2025-07-01 03:01:35.226 if eqi is None:
2025-07-01 03:01:35.242 eqi, eqj = i, j
2025-07-01 03:01:35.250 continue
2025-07-01 03:01:35.261 cruncher.set_seq1(ai)
2025-07-01 03:01:35.274 # computing similarity is expensive, so use the quick
2025-07-01 03:01:35.282 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:35.298 # compares by a factor of 3.
2025-07-01 03:01:35.310 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:35.318 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:35.335 # of the computation is cached by cruncher
2025-07-01 03:01:35.348 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:35.362 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:35.370 cruncher.ratio() > best_ratio:
2025-07-01 03:01:35.382 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:35.394 if best_ratio < cutoff:
2025-07-01 03:01:35.406 # no non-identical "pretty close" pair
2025-07-01 03:01:35.418 if eqi is None:
2025-07-01 03:01:35.430 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:35.446 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:35.462 return
2025-07-01 03:01:35.473 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:35.486 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:35.498 else:
2025-07-01 03:01:35.514 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:35.529 eqi = None
2025-07-01 03:01:35.542
2025-07-01 03:01:35.562 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:35.570 # identical
2025-07-01 03:01:35.582
2025-07-01 03:01:35.591 # pump out diffs from before the synch point
2025-07-01 03:01:35.601 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:35.618
2025-07-01 03:01:35.627 # do intraline marking on the synch pair
2025-07-01 03:01:35.642 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:35.658 if eqi is None:
2025-07-01 03:01:35.671 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:35.685 atags = btags = ""
2025-07-01 03:01:35.698 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:35.710 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:35.723 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:35.734 if tag == 'replace':
2025-07-01 03:01:35.745 atags += '^' * la
2025-07-01 03:01:35.759 btags += '^' * lb
2025-07-01 03:01:35.778 elif tag == 'delete':
2025-07-01 03:01:35.798 atags += '-' * la
2025-07-01 03:01:35.815 elif tag == 'insert':
2025-07-01 03:01:35.830 btags += '+' * lb
2025-07-01 03:01:35.842 elif tag == 'equal':
2025-07-01 03:01:35.859 atags += ' ' * la
2025-07-01 03:01:35.874 btags += ' ' * lb
2025-07-01 03:01:35.885 else:
2025-07-01 03:01:35.897 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:35.914 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:35.931 else:
2025-07-01 03:01:35.946 # the synch pair is identical
2025-07-01 03:01:35.954 yield '  ' + aelt
2025-07-01 03:01:35.966
2025-07-01 03:01:35.983 # pump out diffs from after the synch point
2025-07-01 03:01:35.999 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:36.014
2025-07-01 03:01:36.024 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:36.038 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:36.048
2025-07-01 03:01:36.062 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:36.082 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:36.098 alo = 166, ahi = 1101
2025-07-01 03:01:36.114 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:36.126 blo = 166, bhi = 1101
2025-07-01 03:01:36.134
2025-07-01 03:01:36.145 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:36.163 g = []
2025-07-01 03:01:36.170 if alo < ahi:
2025-07-01 03:01:36.183 if blo < bhi:
2025-07-01 03:01:36.198 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:36.214 else:
2025-07-01 03:01:36.230 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:36.246 elif blo < bhi:
2025-07-01 03:01:36.262 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:36.278
2025-07-01 03:01:36.294 >       yield from g
2025-07-01 03:01:36.310
2025-07-01 03:01:36.326 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:36.342 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:36.358
2025-07-01 03:01:36.383 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:36.399 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:36.409 alo = 166, ahi = 1101
2025-07-01 03:01:36.422 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:36.441 blo = 166, bhi = 1101
2025-07-01 03:01:36.454
2025-07-01 03:01:36.466 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:36.478 r"""
2025-07-01 03:01:36.494 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:36.511 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:36.518 synch point, and intraline difference marking is done on the
2025-07-01 03:01:36.526 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:36.538
2025-07-01 03:01:36.554 Example:
2025-07-01 03:01:36.567
2025-07-01 03:01:36.574 >>> d = Differ()
2025-07-01 03:01:36.590 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:36.607 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:36.614 >>> print(''.join(results), end="")
2025-07-01 03:01:36.630 - abcDefghiJkl
2025-07-01 03:01:36.663 + abcdefGhijkl
2025-07-01 03:01:36.686 """
2025-07-01 03:01:36.698
2025-07-01 03:01:36.714 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:36.730 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:36.746 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:36.762 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:36.778 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:36.794
2025-07-01 03:01:36.810 # search for the pair that matches best without being identical
2025-07-01 03:01:36.834 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:36.846 # on junk -- unless we have to)
2025-07-01 03:01:36.858 for j in range(blo, bhi):
2025-07-01 03:01:36.870 bj = b[j]
2025-07-01 03:01:36.886 cruncher.set_seq2(bj)
2025-07-01 03:01:36.898 for i in range(alo, ahi):
2025-07-01 03:01:36.910 ai = a[i]
2025-07-01 03:01:36.934 if ai == bj:
2025-07-01 03:01:36.942 if eqi is None:
2025-07-01 03:01:36.953 eqi, eqj = i, j
2025-07-01 03:01:36.962 continue
2025-07-01 03:01:36.978 cruncher.set_seq1(ai)
2025-07-01 03:01:36.990 # computing similarity is expensive, so use the quick
2025-07-01 03:01:37.010 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:37.017 # compares by a factor of 3.
2025-07-01 03:01:37.030 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:37.046 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:37.063 # of the computation is cached by cruncher
2025-07-01 03:01:37.074 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:37.086 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:37.098 cruncher.ratio() > best_ratio:
2025-07-01 03:01:37.114 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:37.122 if best_ratio < cutoff:
2025-07-01 03:01:37.134 # no non-identical "pretty close" pair
2025-07-01 03:01:37.142 if eqi is None:
2025-07-01 03:01:37.163 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:37.178 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:37.195 return
2025-07-01 03:01:37.212 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:37.226 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:37.234 else:
2025-07-01 03:01:37.250 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:37.258 eqi = None
2025-07-01 03:01:37.266
2025-07-01 03:01:37.279 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:37.296 # identical
2025-07-01 03:01:37.314
2025-07-01 03:01:37.322 # pump out diffs from before the synch point
2025-07-01 03:01:37.337 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:37.346
2025-07-01 03:01:37.361 # do intraline marking on the synch pair
2025-07-01 03:01:37.374 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:37.387 if eqi is None:
2025-07-01 03:01:37.406 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:37.419 atags = btags = ""
2025-07-01 03:01:37.438 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:37.451 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:37.466 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:37.478 if tag == 'replace':
2025-07-01 03:01:37.494 atags += '^' * la
2025-07-01 03:01:37.505 btags += '^' * lb
2025-07-01 03:01:37.518 elif tag == 'delete':
2025-07-01 03:01:37.530 atags += '-' * la
2025-07-01 03:01:37.542 elif tag == 'insert':
2025-07-01 03:01:37.558 btags += '+' * lb
2025-07-01 03:01:37.574 elif tag == 'equal':
2025-07-01 03:01:37.590 atags += ' ' * la
2025-07-01 03:01:37.599 btags += ' ' * lb
2025-07-01 03:01:37.609 else:
2025-07-01 03:01:37.621 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:37.634 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:37.649 else:
2025-07-01 03:01:37.662 # the synch pair is identical
2025-07-01 03:01:37.674 yield '  ' + aelt
2025-07-01 03:01:37.686
2025-07-01 03:01:37.698 # pump out diffs from after the synch point
2025-07-01 03:01:37.706 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:37.718
2025-07-01 03:01:37.726 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:37.738 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:37.750
2025-07-01 03:01:37.758 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:37.772 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:37.790 alo = 167, ahi = 1101
2025-07-01 03:01:37.806 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:37.814 blo = 167, bhi = 1101
2025-07-01 03:01:37.822
2025-07-01 03:01:37.834 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:37.842 g = []
2025-07-01 03:01:37.854 if alo < ahi:
2025-07-01 03:01:37.862 if blo < bhi:
2025-07-01 03:01:37.874 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:37.882 else:
2025-07-01 03:01:37.894 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:37.902 elif blo < bhi:
2025-07-01 03:01:37.914 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:37.926
2025-07-01 03:01:37.937 >       yield from g
2025-07-01 03:01:37.946
2025-07-01 03:01:37.952 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:37.963 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:37.974
2025-07-01 03:01:37.982 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:37.990 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:38.002 alo = 167, ahi = 1101
2025-07-01 03:01:38.018 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:38.026 blo = 167, bhi = 1101
2025-07-01 03:01:38.038
2025-07-01 03:01:38.045 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:38.056 r"""
2025-07-01 03:01:38.074 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:38.087 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:38.102 synch point, and intraline difference marking is done on the
2025-07-01 03:01:38.114 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:38.130
2025-07-01 03:01:38.140 Example:
2025-07-01 03:01:38.150
2025-07-01 03:01:38.166 >>> d = Differ()
2025-07-01 03:01:38.174 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:38.186 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:38.193 >>> print(''.join(results), end="")
2025-07-01 03:01:38.202 - abcDefghiJkl
2025-07-01 03:01:38.229 + abcdefGhijkl
2025-07-01 03:01:38.254 """
2025-07-01 03:01:38.262
2025-07-01 03:01:38.270 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:38.282 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:38.294 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:38.306 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:38.325 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:38.337
2025-07-01 03:01:38.349 # search for the pair that matches best without being identical
2025-07-01 03:01:38.362 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:38.370 # on junk -- unless we have to)
2025-07-01 03:01:38.380 for j in range(blo, bhi):
2025-07-01 03:01:38.394 bj = b[j]
2025-07-01 03:01:38.406 cruncher.set_seq2(bj)
2025-07-01 03:01:38.418 for i in range(alo, ahi):
2025-07-01 03:01:38.434 ai = a[i]
2025-07-01 03:01:38.450 if ai == bj:
2025-07-01 03:01:38.461 if eqi is None:
2025-07-01 03:01:38.470 eqi, eqj = i, j
2025-07-01 03:01:38.482 continue
2025-07-01 03:01:38.490 cruncher.set_seq1(ai)
2025-07-01 03:01:38.502 # computing similarity is expensive, so use the quick
2025-07-01 03:01:38.514 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:38.522 # compares by a factor of 3.
2025-07-01 03:01:38.534 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:38.542 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:38.560 # of the computation is cached by cruncher
2025-07-01 03:01:38.574 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:38.585 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:38.601 cruncher.ratio() > best_ratio:
2025-07-01 03:01:38.610 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:38.616 if best_ratio < cutoff:
2025-07-01 03:01:38.630 # no non-identical "pretty close" pair
2025-07-01 03:01:38.640 if eqi is None:
2025-07-01 03:01:38.645 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:38.650 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:38.656 return
2025-07-01 03:01:38.662 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:38.668 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:38.672 else:
2025-07-01 03:01:38.678 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:38.685 eqi = None
2025-07-01 03:01:38.689
2025-07-01 03:01:38.694 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:38.698 # identical
2025-07-01 03:01:38.702
2025-07-01 03:01:38.707 # pump out diffs from before the synch point
2025-07-01 03:01:38.711 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:38.716
2025-07-01 03:01:38.720 # do intraline marking on the synch pair
2025-07-01 03:01:38.725 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:38.730 if eqi is None:
2025-07-01 03:01:38.735 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:38.739 atags = btags = ""
2025-07-01 03:01:38.744 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:38.748 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:38.753 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:38.757 if tag == 'replace':
2025-07-01 03:01:38.762 atags += '^' * la
2025-07-01 03:01:38.766 btags += '^' * lb
2025-07-01 03:01:38.770 elif tag == 'delete':
2025-07-01 03:01:38.777 atags += '-' * la
2025-07-01 03:01:38.781 elif tag == 'insert':
2025-07-01 03:01:38.786 btags += '+' * lb
2025-07-01 03:01:38.790 elif tag == 'equal':
2025-07-01 03:01:38.795 atags += ' ' * la
2025-07-01 03:01:38.799 btags += ' ' * lb
2025-07-01 03:01:38.804 else:
2025-07-01 03:01:38.809 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:38.813 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:38.818 else:
2025-07-01 03:01:38.822 # the synch pair is identical
2025-07-01 03:01:38.827 yield '  ' + aelt
2025-07-01 03:01:38.831
2025-07-01 03:01:38.835 # pump out diffs from after the synch point
2025-07-01 03:01:38.840 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:38.845
2025-07-01 03:01:38.849 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:38.854 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:38.858
2025-07-01 03:01:38.863 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:38.868 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:38.872 alo = 168, ahi = 1101
2025-07-01 03:01:38.878 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:38.882 blo = 168, bhi = 1101
2025-07-01 03:01:38.886
2025-07-01 03:01:38.891 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:38.895 g = []
2025-07-01 03:01:38.899 if alo < ahi:
2025-07-01 03:01:38.904 if blo < bhi:
2025-07-01 03:01:38.909 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:38.913 else:
2025-07-01 03:01:38.917 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:38.922 elif blo < bhi:
2025-07-01 03:01:38.926 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:38.931
2025-07-01 03:01:38.935 >       yield from g
2025-07-01 03:01:38.940
2025-07-01 03:01:38.944 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:38.949 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:38.954
2025-07-01 03:01:38.960 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:38.965 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:38.970 alo = 168, ahi = 1101
2025-07-01 03:01:38.975 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:38.979 blo = 168, bhi = 1101
2025-07-01 03:01:38.983
2025-07-01 03:01:38.988 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:38.992 r"""
2025-07-01 03:01:38.996 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:39.001 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:39.006 synch point, and intraline difference marking is done on the
2025-07-01 03:01:39.010 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:39.014
2025-07-01 03:01:39.019 Example:
2025-07-01 03:01:39.023
2025-07-01 03:01:39.027 >>> d = Differ()
2025-07-01 03:01:39.032 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:39.036 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:39.041 >>> print(''.join(results), end="")
2025-07-01 03:01:39.046 - abcDefghiJkl
2025-07-01 03:01:39.054 + abcdefGhijkl
2025-07-01 03:01:39.063 """
2025-07-01 03:01:39.067
2025-07-01 03:01:39.071 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:39.076 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:39.083 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:39.088 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:39.093 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:39.097
2025-07-01 03:01:39.102 # search for the pair that matches best without being identical
2025-07-01 03:01:39.106 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:39.111 # on junk -- unless we have to)
2025-07-01 03:01:39.115 for j in range(blo, bhi):
2025-07-01 03:01:39.119 bj = b[j]
2025-07-01 03:01:39.124 cruncher.set_seq2(bj)
2025-07-01 03:01:39.129 for i in range(alo, ahi):
2025-07-01 03:01:39.133 ai = a[i]
2025-07-01 03:01:39.137 if ai == bj:
2025-07-01 03:01:39.142 if eqi is None:
2025-07-01 03:01:39.146 eqi, eqj = i, j
2025-07-01 03:01:39.150 continue
2025-07-01 03:01:39.155 cruncher.set_seq1(ai)
2025-07-01 03:01:39.159 # computing similarity is expensive, so use the quick
2025-07-01 03:01:39.164 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:39.168 # compares by a factor of 3.
2025-07-01 03:01:39.172 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:39.177 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:39.182 # of the computation is cached by cruncher
2025-07-01 03:01:39.186 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:39.191 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:39.196 cruncher.ratio() > best_ratio:
2025-07-01 03:01:39.202 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:39.207 if best_ratio < cutoff:
2025-07-01 03:01:39.212 # no non-identical "pretty close" pair
2025-07-01 03:01:39.216 if eqi is None:
2025-07-01 03:01:39.221 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:39.226 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:39.231 return
2025-07-01 03:01:39.235 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:39.240 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:39.244 else:
2025-07-01 03:01:39.249 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:39.254 eqi = None
2025-07-01 03:01:39.258
2025-07-01 03:01:39.263 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:39.267 # identical
2025-07-01 03:01:39.272
2025-07-01 03:01:39.276 # pump out diffs from before the synch point
2025-07-01 03:01:39.281 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:39.285
2025-07-01 03:01:39.289 # do intraline marking on the synch pair
2025-07-01 03:01:39.294 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:39.298 if eqi is None:
2025-07-01 03:01:39.303 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:39.307 atags = btags = ""
2025-07-01 03:01:39.312 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:39.317 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:39.321 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:39.326 if tag == 'replace':
2025-07-01 03:01:39.330 atags += '^' * la
2025-07-01 03:01:39.335 btags += '^' * lb
2025-07-01 03:01:39.339 elif tag == 'delete':
2025-07-01 03:01:39.344 atags += '-' * la
2025-07-01 03:01:39.348 elif tag == 'insert':
2025-07-01 03:01:39.353 btags += '+' * lb
2025-07-01 03:01:39.357 elif tag == 'equal':
2025-07-01 03:01:39.362 atags += ' ' * la
2025-07-01 03:01:39.367 btags += ' ' * lb
2025-07-01 03:01:39.371 else:
2025-07-01 03:01:39.377 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:39.382 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:39.386 else:
2025-07-01 03:01:39.391 # the synch pair is identical
2025-07-01 03:01:39.396 yield '  ' + aelt
2025-07-01 03:01:39.400
2025-07-01 03:01:39.405 # pump out diffs from after the synch point
2025-07-01 03:01:39.409 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:39.413
2025-07-01 03:01:39.418 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:39.424 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:39.429
2025-07-01 03:01:39.434 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:39.439 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:39.444 alo = 169, ahi = 1101
2025-07-01 03:01:39.449 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:39.453 blo = 169, bhi = 1101
2025-07-01 03:01:39.458
2025-07-01 03:01:39.463 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:39.468 g = []
2025-07-01 03:01:39.473 if alo < ahi:
2025-07-01 03:01:39.477 if blo < bhi:
2025-07-01 03:01:39.481 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:39.486 else:
2025-07-01 03:01:39.490 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:39.495 elif blo < bhi:
2025-07-01 03:01:39.499 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:39.504
2025-07-01 03:01:39.508 >       yield from g
2025-07-01 03:01:39.514
2025-07-01 03:01:39.519 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:39.523 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:39.530
2025-07-01 03:01:39.535 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:39.540 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:39.544 alo = 169, ahi = 1101
2025-07-01 03:01:39.549 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:39.553 blo = 169, bhi = 1101
2025-07-01 03:01:39.558
2025-07-01 03:01:39.562 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:39.566 r"""
2025-07-01 03:01:39.571 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:39.575 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:39.580 synch point, and intraline difference marking is done on the
2025-07-01 03:01:39.584 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:39.589
2025-07-01 03:01:39.593 Example:
2025-07-01 03:01:39.597
2025-07-01 03:01:39.602 >>> d = Differ()
2025-07-01 03:01:39.606 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:39.611 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:39.615 >>> print(''.join(results), end="")
2025-07-01 03:01:39.619 - abcDefghiJkl
2025-07-01 03:01:39.628 + abcdefGhijkl
2025-07-01 03:01:39.636 """
2025-07-01 03:01:39.641
2025-07-01 03:01:39.645 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:39.650 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:39.655 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:39.666 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:39.678 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:39.690
2025-07-01 03:01:39.702 # search for the pair that matches best without being identical
2025-07-01 03:01:39.714 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:39.726 # on junk -- unless we have to)
2025-07-01 03:01:39.739 for j in range(blo, bhi):
2025-07-01 03:01:39.754 bj = b[j]
2025-07-01 03:01:39.761 cruncher.set_seq2(bj)
2025-07-01 03:01:39.773 for i in range(alo, ahi):
2025-07-01 03:01:39.786 ai = a[i]
2025-07-01 03:01:39.796 if ai == bj:
2025-07-01 03:01:39.814 if eqi is None:
2025-07-01 03:01:39.826 eqi, eqj = i, j
2025-07-01 03:01:39.834 continue
2025-07-01 03:01:39.846 cruncher.set_seq1(ai)
2025-07-01 03:01:39.854 # computing similarity is expensive, so use the quick
2025-07-01 03:01:39.865 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:39.878 # compares by a factor of 3.
2025-07-01 03:01:39.890 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:39.901 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:39.910 # of the computation is cached by cruncher
2025-07-01 03:01:39.917 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:39.923 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:39.930 cruncher.ratio() > best_ratio:
2025-07-01 03:01:39.942 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:39.954 if best_ratio < cutoff:
2025-07-01 03:01:39.967 # no non-identical "pretty close" pair
2025-07-01 03:01:39.979 if eqi is None:
2025-07-01 03:01:39.997 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:40.014 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:40.034 return
2025-07-01 03:01:40.046 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:40.057 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:40.070 else:
2025-07-01 03:01:40.079 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:40.090 eqi = None
2025-07-01 03:01:40.098
2025-07-01 03:01:40.110 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:40.118 # identical
2025-07-01 03:01:40.130
2025-07-01 03:01:40.138 # pump out diffs from before the synch point
2025-07-01 03:01:40.150 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:40.157
2025-07-01 03:01:40.170 # do intraline marking on the synch pair
2025-07-01 03:01:40.182 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:40.190 if eqi is None:
2025-07-01 03:01:40.202 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:40.210 atags = btags = ""
2025-07-01 03:01:40.218 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:40.230 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:40.238 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:40.254 if tag == 'replace':
2025-07-01 03:01:40.262 atags += '^' * la
2025-07-01 03:01:40.274 btags += '^' * lb
2025-07-01 03:01:40.286 elif tag == 'delete':
2025-07-01 03:01:40.302 atags += '-' * la
2025-07-01 03:01:40.310 elif tag == 'insert':
2025-07-01 03:01:40.314 btags += '+' * lb
2025-07-01 03:01:40.319 elif tag == 'equal':
2025-07-01 03:01:40.323 atags += ' ' * la
2025-07-01 03:01:40.328 btags += ' ' * lb
2025-07-01 03:01:40.332 else:
2025-07-01 03:01:40.336 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:40.341 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:40.345 else:
2025-07-01 03:01:40.350 # the synch pair is identical
2025-07-01 03:01:40.354 yield '  ' + aelt
2025-07-01 03:01:40.358
2025-07-01 03:01:40.363 # pump out diffs from after the synch point
2025-07-01 03:01:40.367 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:40.372
2025-07-01 03:01:40.376 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:40.394 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:40.404
2025-07-01 03:01:40.414 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:40.426 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:40.438 alo = 170, ahi = 1101
2025-07-01 03:01:40.450 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:40.457 blo = 170, bhi = 1101
2025-07-01 03:01:40.470
2025-07-01 03:01:40.485 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:40.494 g = []
2025-07-01 03:01:40.507 if alo < ahi:
2025-07-01 03:01:40.518 if blo < bhi:
2025-07-01 03:01:40.534 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:40.542 else:
2025-07-01 03:01:40.558 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:40.566 elif blo < bhi:
2025-07-01 03:01:40.582 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:40.590
2025-07-01 03:01:40.606 >       yield from g
2025-07-01 03:01:40.614
2025-07-01 03:01:40.630 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:40.642 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:40.654
2025-07-01 03:01:40.662 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:40.674 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:40.683 alo = 170, ahi = 1101
2025-07-01 03:01:40.698 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:40.710 blo = 170, bhi = 1101
2025-07-01 03:01:40.721
2025-07-01 03:01:40.734 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:40.746 r"""
2025-07-01 03:01:40.758 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:40.766 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:40.778 synch point, and intraline difference marking is done on the
2025-07-01 03:01:40.786 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:40.798
2025-07-01 03:01:40.808 Example:
2025-07-01 03:01:40.822
2025-07-01 03:01:40.834 >>> d = Differ()
2025-07-01 03:01:40.842 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:40.853 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:40.870 >>> print(''.join(results), end="")
2025-07-01 03:01:40.885 - abcDefghiJkl
2025-07-01 03:01:40.909 + abcdefGhijkl
2025-07-01 03:01:40.940 """
2025-07-01 03:01:40.951
2025-07-01 03:01:40.966 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:40.979 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:40.989 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:41.001 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:41.007 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:41.013
2025-07-01 03:01:41.018 # search for the pair that matches best without being identical
2025-07-01 03:01:41.026 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:41.031 # on junk -- unless we have to)
2025-07-01 03:01:41.035 for j in range(blo, bhi):
2025-07-01 03:01:41.040 bj = b[j]
2025-07-01 03:01:41.044 cruncher.set_seq2(bj)
2025-07-01 03:01:41.053 for i in range(alo, ahi):
2025-07-01 03:01:41.061 ai = a[i]
2025-07-01 03:01:41.066 if ai == bj:
2025-07-01 03:01:41.070 if eqi is None:
2025-07-01 03:01:41.075 eqi, eqj = i, j
2025-07-01 03:01:41.079 continue
2025-07-01 03:01:41.084 cruncher.set_seq1(ai)
2025-07-01 03:01:41.089 # computing similarity is expensive, so use the quick
2025-07-01 03:01:41.093 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:41.098 # compares by a factor of 3.
2025-07-01 03:01:41.103 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:41.108 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:41.113 # of the computation is cached by cruncher
2025-07-01 03:01:41.118 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:41.122 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:41.127 cruncher.ratio() > best_ratio:
2025-07-01 03:01:41.131 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:41.136 if best_ratio < cutoff:
2025-07-01 03:01:41.141 # no non-identical "pretty close" pair
2025-07-01 03:01:41.145 if eqi is None:
2025-07-01 03:01:41.150 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:41.154 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:41.159 return
2025-07-01 03:01:41.163 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:41.168 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:41.172 else:
2025-07-01 03:01:41.177 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:41.182 eqi = None
2025-07-01 03:01:41.186
2025-07-01 03:01:41.191 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:41.195 # identical
2025-07-01 03:01:41.200
2025-07-01 03:01:41.205 # pump out diffs from before the synch point
2025-07-01 03:01:41.209 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:41.214
2025-07-01 03:01:41.218 # do intraline marking on the synch pair
2025-07-01 03:01:41.223 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:41.228 if eqi is None:
2025-07-01 03:01:41.232 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:41.237 atags = btags = ""
2025-07-01 03:01:41.242 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:41.246 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:41.251 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:41.255 if tag == 'replace':
2025-07-01 03:01:41.260 atags += '^' * la
2025-07-01 03:01:41.265 btags += '^' * lb
2025-07-01 03:01:41.269 elif tag == 'delete':
2025-07-01 03:01:41.274 atags += '-' * la
2025-07-01 03:01:41.278 elif tag == 'insert':
2025-07-01 03:01:41.282 btags += '+' * lb
2025-07-01 03:01:41.287 elif tag == 'equal':
2025-07-01 03:01:41.291 atags += ' ' * la
2025-07-01 03:01:41.296 btags += ' ' * lb
2025-07-01 03:01:41.300 else:
2025-07-01 03:01:41.305 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:41.310 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:41.314 else:
2025-07-01 03:01:41.319 # the synch pair is identical
2025-07-01 03:01:41.323 yield '  ' + aelt
2025-07-01 03:01:41.328
2025-07-01 03:01:41.332 # pump out diffs from after the synch point
2025-07-01 03:01:41.337 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:41.341
2025-07-01 03:01:41.346 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:41.350 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:41.355
2025-07-01 03:01:41.359 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:41.364 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:41.368 alo = 171, ahi = 1101
2025-07-01 03:01:41.373 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:41.377 blo = 171, bhi = 1101
2025-07-01 03:01:41.382
2025-07-01 03:01:41.386 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:41.391 g = []
2025-07-01 03:01:41.395 if alo < ahi:
2025-07-01 03:01:41.400 if blo < bhi:
2025-07-01 03:01:41.404 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:41.408 else:
2025-07-01 03:01:41.413 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:41.418 elif blo < bhi:
2025-07-01 03:01:41.423 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:41.427
2025-07-01 03:01:41.433 >       yield from g
2025-07-01 03:01:41.441
2025-07-01 03:01:41.450 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:41.458 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:41.472
2025-07-01 03:01:41.483 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:41.489 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:41.502 alo = 171, ahi = 1101
2025-07-01 03:01:41.520 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:41.534 blo = 171, bhi = 1101
2025-07-01 03:01:41.542
2025-07-01 03:01:41.554 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:41.566 r"""
2025-07-01 03:01:41.576 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:41.590 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:41.602 synch point, and intraline difference marking is done on the
2025-07-01 03:01:41.610 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:41.618
2025-07-01 03:01:41.626 Example:
2025-07-01 03:01:41.642
2025-07-01 03:01:41.650 >>> d = Differ()
2025-07-01 03:01:41.662 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:41.670 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:41.686 >>> print(''.join(results), end="")
2025-07-01 03:01:41.694 - abcDefghiJkl
2025-07-01 03:01:41.722 + abcdefGhijkl
2025-07-01 03:01:41.742 """
2025-07-01 03:01:41.750
2025-07-01 03:01:41.766 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:41.778 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:41.786 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:41.798 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:41.806 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:41.818
2025-07-01 03:01:41.826 # search for the pair that matches best without being identical
2025-07-01 03:01:41.838 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:41.850 # on junk -- unless we have to)
2025-07-01 03:01:41.858 for j in range(blo, bhi):
2025-07-01 03:01:41.874 bj = b[j]
2025-07-01 03:01:41.882 cruncher.set_seq2(bj)
2025-07-01 03:01:41.898 for i in range(alo, ahi):
2025-07-01 03:01:41.906 ai = a[i]
2025-07-01 03:01:41.918 if ai == bj:
2025-07-01 03:01:41.930 if eqi is None:
2025-07-01 03:01:41.938 eqi, eqj = i, j
2025-07-01 03:01:41.950 continue
2025-07-01 03:01:41.966 cruncher.set_seq1(ai)
2025-07-01 03:01:41.974 # computing similarity is expensive, so use the quick
2025-07-01 03:01:41.982 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:41.994 # compares by a factor of 3.
2025-07-01 03:01:42.002 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:42.014 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:42.026 # of the computation is cached by cruncher
2025-07-01 03:01:42.034 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:42.046 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:42.054 cruncher.ratio() > best_ratio:
2025-07-01 03:01:42.070 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:42.078 if best_ratio < cutoff:
2025-07-01 03:01:42.094 # no non-identical "pretty close" pair
2025-07-01 03:01:42.102 if eqi is None:
2025-07-01 03:01:42.118 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:42.126 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:42.141 return
2025-07-01 03:01:42.156 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:42.164 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:42.178 else:
2025-07-01 03:01:42.190 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:42.198 eqi = None
2025-07-01 03:01:42.210
2025-07-01 03:01:42.222 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:42.229 # identical
2025-07-01 03:01:42.242
2025-07-01 03:01:42.258 # pump out diffs from before the synch point
2025-07-01 03:01:42.274 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:42.290
2025-07-01 03:01:42.298 # do intraline marking on the synch pair
2025-07-01 03:01:42.310 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:42.326 if eqi is None:
2025-07-01 03:01:42.337 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:42.350 atags = btags = ""
2025-07-01 03:01:42.362 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:42.378 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:42.390 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:42.397 if tag == 'replace':
2025-07-01 03:01:42.406 atags += '^' * la
2025-07-01 03:01:42.415 btags += '^' * lb
2025-07-01 03:01:42.430 elif tag == 'delete':
2025-07-01 03:01:42.446 atags += '-' * la
2025-07-01 03:01:42.453 elif tag == 'insert':
2025-07-01 03:01:42.461 btags += '+' * lb
2025-07-01 03:01:42.478 elif tag == 'equal':
2025-07-01 03:01:42.486 atags += ' ' * la
2025-07-01 03:01:42.498 btags += ' ' * lb
2025-07-01 03:01:42.510 else:
2025-07-01 03:01:42.522 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:42.530 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:42.544 else:
2025-07-01 03:01:42.558 # the synch pair is identical
2025-07-01 03:01:42.570 yield '  ' + aelt
2025-07-01 03:01:42.582
2025-07-01 03:01:42.598 # pump out diffs from after the synch point
2025-07-01 03:01:42.610 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:42.619
2025-07-01 03:01:42.626 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:42.638 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:42.650
2025-07-01 03:01:42.658 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:42.670 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:42.682 alo = 172, ahi = 1101
2025-07-01 03:01:42.698 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:42.706 blo = 172, bhi = 1101
2025-07-01 03:01:42.718
2025-07-01 03:01:42.726 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:42.737 g = []
2025-07-01 03:01:42.750 if alo < ahi:
2025-07-01 03:01:42.758 if blo < bhi:
2025-07-01 03:01:42.768 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:42.779 else:
2025-07-01 03:01:42.792 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:42.806 elif blo < bhi:
2025-07-01 03:01:42.816 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:42.830
2025-07-01 03:01:42.835 >       yield from g
2025-07-01 03:01:42.841
2025-07-01 03:01:42.846 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:42.850 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:42.855
2025-07-01 03:01:42.859 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:42.864 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:42.869 alo = 172, ahi = 1101
2025-07-01 03:01:42.874 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:42.881 blo = 172, bhi = 1101
2025-07-01 03:01:42.886
2025-07-01 03:01:42.890 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:42.895 r"""
2025-07-01 03:01:42.899 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:42.904 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:42.913 synch point, and intraline difference marking is done on the
2025-07-01 03:01:42.934 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:42.948
2025-07-01 03:01:42.963 Example:
2025-07-01 03:01:42.973
2025-07-01 03:01:42.988 >>> d = Differ()
2025-07-01 03:01:43.000 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:43.015 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:43.033 >>> print(''.join(results), end="")
2025-07-01 03:01:43.049 - abcDefghiJkl
2025-07-01 03:01:43.083 + abcdefGhijkl
2025-07-01 03:01:43.110 """
2025-07-01 03:01:43.126
2025-07-01 03:01:43.133 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:43.146 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:43.153 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:43.165 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:43.173 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:43.186
2025-07-01 03:01:43.194 # search for the pair that matches best without being identical
2025-07-01 03:01:43.208 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:43.218 # on junk -- unless we have to)
2025-07-01 03:01:43.230 for j in range(blo, bhi):
2025-07-01 03:01:43.237 bj = b[j]
2025-07-01 03:01:43.250 cruncher.set_seq2(bj)
2025-07-01 03:01:43.258 for i in range(alo, ahi):
2025-07-01 03:01:43.270 ai = a[i]
2025-07-01 03:01:43.282 if ai == bj:
2025-07-01 03:01:43.290 if eqi is None:
2025-07-01 03:01:43.302 eqi, eqj = i, j
2025-07-01 03:01:43.310 continue
2025-07-01 03:01:43.324 cruncher.set_seq1(ai)
2025-07-01 03:01:43.334 # computing similarity is expensive, so use the quick
2025-07-01 03:01:43.351 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:43.362 # compares by a factor of 3.
2025-07-01 03:01:43.374 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:43.380 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:43.390 # of the computation is cached by cruncher
2025-07-01 03:01:43.403 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:43.414 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:43.427 cruncher.ratio() > best_ratio:
2025-07-01 03:01:43.434 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:43.446 if best_ratio < cutoff:
2025-07-01 03:01:43.455 # no non-identical "pretty close" pair
2025-07-01 03:01:43.471 if eqi is None:
2025-07-01 03:01:43.485 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:43.498 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:43.506 return
2025-07-01 03:01:43.519 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:43.533 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:43.546 else:
2025-07-01 03:01:43.559 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:43.575 eqi = None
2025-07-01 03:01:43.586
2025-07-01 03:01:43.594 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:43.614 # identical
2025-07-01 03:01:43.626
2025-07-01 03:01:43.643 # pump out diffs from before the synch point
2025-07-01 03:01:43.658 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:43.670
2025-07-01 03:01:43.686 # do intraline marking on the synch pair
2025-07-01 03:01:43.702 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:43.710 if eqi is None:
2025-07-01 03:01:43.722 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:43.734 atags = btags = ""
2025-07-01 03:01:43.750 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:43.758 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:43.770 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:43.786 if tag == 'replace':
2025-07-01 03:01:43.798 atags += '^' * la
2025-07-01 03:01:43.814 btags += '^' * lb
2025-07-01 03:01:43.830 elif tag == 'delete':
2025-07-01 03:01:43.838 atags += '-' * la
2025-07-01 03:01:43.846 elif tag == 'insert':
2025-07-01 03:01:43.858 btags += '+' * lb
2025-07-01 03:01:43.866 elif tag == 'equal':
2025-07-01 03:01:43.882 atags += ' ' * la
2025-07-01 03:01:43.898 btags += ' ' * lb
2025-07-01 03:01:43.906 else:
2025-07-01 03:01:43.914 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:43.930 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:43.938 else:
2025-07-01 03:01:43.950 # the synch pair is identical
2025-07-01 03:01:43.958 yield '  ' + aelt
2025-07-01 03:01:43.970
2025-07-01 03:01:43.978 # pump out diffs from after the synch point
2025-07-01 03:01:43.988 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:43.998
2025-07-01 03:01:44.018 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:44.026 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:44.041
2025-07-01 03:01:44.054 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:44.070 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:44.082 alo = 173, ahi = 1101
2025-07-01 03:01:44.098 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:44.110 blo = 173, bhi = 1101
2025-07-01 03:01:44.122
2025-07-01 03:01:44.134 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:44.142 g = []
2025-07-01 03:01:44.154 if alo < ahi:
2025-07-01 03:01:44.166 if blo < bhi:
2025-07-01 03:01:44.174 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:44.190 else:
2025-07-01 03:01:44.202 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:44.210 elif blo < bhi:
2025-07-01 03:01:44.221 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:44.230
2025-07-01 03:01:44.241 >       yield from g
2025-07-01 03:01:44.253
2025-07-01 03:01:44.266 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:44.272 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:44.282
2025-07-01 03:01:44.298 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:44.314 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:44.320 alo = 173, ahi = 1101
2025-07-01 03:01:44.334 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:44.342 blo = 173, bhi = 1101
2025-07-01 03:01:44.354
2025-07-01 03:01:44.366 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:44.374 r"""
2025-07-01 03:01:44.386 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:44.398 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:44.406 synch point, and intraline difference marking is done on the
2025-07-01 03:01:44.418 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:44.426
2025-07-01 03:01:44.438 Example:
2025-07-01 03:01:44.446
2025-07-01 03:01:44.458 >>> d = Differ()
2025-07-01 03:01:44.469 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:44.478 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:44.488 >>> print(''.join(results), end="")
2025-07-01 03:01:44.498 - abcDefghiJkl
2025-07-01 03:01:44.524 + abcdefGhijkl
2025-07-01 03:01:44.548 """
2025-07-01 03:01:44.558
2025-07-01 03:01:44.570 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:44.582 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:44.590 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:44.602 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:44.614 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:44.626
2025-07-01 03:01:44.634 # search for the pair that matches best without being identical
2025-07-01 03:01:44.650 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:44.662 # on junk -- unless we have to)
2025-07-01 03:01:44.678 for j in range(blo, bhi):
2025-07-01 03:01:44.690 bj = b[j]
2025-07-01 03:01:44.706 cruncher.set_seq2(bj)
2025-07-01 03:01:44.713 for i in range(alo, ahi):
2025-07-01 03:01:44.730 ai = a[i]
2025-07-01 03:01:44.742 if ai == bj:
2025-07-01 03:01:44.754 if eqi is None:
2025-07-01 03:01:44.766 eqi, eqj = i, j
2025-07-01 03:01:44.774 continue
2025-07-01 03:01:44.786 cruncher.set_seq1(ai)
2025-07-01 03:01:44.794 # computing similarity is expensive, so use the quick
2025-07-01 03:01:44.805 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:44.817 # compares by a factor of 3.
2025-07-01 03:01:44.826 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:44.837 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:44.849 # of the computation is cached by cruncher
2025-07-01 03:01:44.858 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:44.874 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:44.884 cruncher.ratio() > best_ratio:
2025-07-01 03:01:44.898 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:44.910 if best_ratio < cutoff:
2025-07-01 03:01:44.922 # no non-identical "pretty close" pair
2025-07-01 03:01:44.930 if eqi is None:
2025-07-01 03:01:44.946 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:44.958 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:44.966 return
2025-07-01 03:01:44.977 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:44.994 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:45.002 else:
2025-07-01 03:01:45.014 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:45.026 eqi = None
2025-07-01 03:01:45.040
2025-07-01 03:01:45.050 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:45.066 # identical
2025-07-01 03:01:45.074
2025-07-01 03:01:45.090 # pump out diffs from before the synch point
2025-07-01 03:01:45.098 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:45.114
2025-07-01 03:01:45.130 # do intraline marking on the synch pair
2025-07-01 03:01:45.142 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:45.154 if eqi is None:
2025-07-01 03:01:45.162 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:45.174 atags = btags = ""
2025-07-01 03:01:45.186 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:45.194 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:45.209 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:45.219 if tag == 'replace':
2025-07-01 03:01:45.226 atags += '^' * la
2025-07-01 03:01:45.238 btags += '^' * lb
2025-07-01 03:01:45.250 elif tag == 'delete':
2025-07-01 03:01:45.258 atags += '-' * la
2025-07-01 03:01:45.274 elif tag == 'insert':
2025-07-01 03:01:45.289 btags += '+' * lb
2025-07-01 03:01:45.302 elif tag == 'equal':
2025-07-01 03:01:45.317 atags += ' ' * la
2025-07-01 03:01:45.330 btags += ' ' * lb
2025-07-01 03:01:45.345 else:
2025-07-01 03:01:45.354 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:45.366 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:45.378 else:
2025-07-01 03:01:45.389 # the synch pair is identical
2025-07-01 03:01:45.405 yield '  ' + aelt
2025-07-01 03:01:45.410
2025-07-01 03:01:45.419 # pump out diffs from after the synch point
2025-07-01 03:01:45.432 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:45.441
2025-07-01 03:01:45.458 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:45.466 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:45.478
2025-07-01 03:01:45.486 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:45.498 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:45.511 alo = 174, ahi = 1101
2025-07-01 03:01:45.531 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:45.543 blo = 174, bhi = 1101
2025-07-01 03:01:45.558
2025-07-01 03:01:45.570 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:45.582 g = []
2025-07-01 03:01:45.590 if alo < ahi:
2025-07-01 03:01:45.602 if blo < bhi:
2025-07-01 03:01:45.614 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:45.622 else:
2025-07-01 03:01:45.634 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:45.642 elif blo < bhi:
2025-07-01 03:01:45.654 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:45.664
2025-07-01 03:01:45.678 >       yield from g
2025-07-01 03:01:45.690
2025-07-01 03:01:45.698 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:45.710 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:45.718
2025-07-01 03:01:45.730 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:45.746 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:45.754 alo = 174, ahi = 1101
2025-07-01 03:01:45.762 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:45.774 blo = 174, bhi = 1101
2025-07-01 03:01:45.782
2025-07-01 03:01:45.794 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:45.806 r"""
2025-07-01 03:01:45.813 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:45.826 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:45.834 synch point, and intraline difference marking is done on the
2025-07-01 03:01:45.846 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:45.854
2025-07-01 03:01:45.860 Example:
2025-07-01 03:01:45.872
2025-07-01 03:01:45.881 >>> d = Differ()
2025-07-01 03:01:45.898 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:45.907 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:45.917 >>> print(''.join(results), end="")
2025-07-01 03:01:45.923 - abcDefghiJkl
2025-07-01 03:01:45.935 + abcdefGhijkl
2025-07-01 03:01:45.946 """
2025-07-01 03:01:45.951
2025-07-01 03:01:45.957 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:45.962 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:45.968 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:45.975 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:45.982 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:45.990
2025-07-01 03:01:45.997 # search for the pair that matches best without being identical
2025-07-01 03:01:46.003 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:46.009 # on junk -- unless we have to)
2025-07-01 03:01:46.016 for j in range(blo, bhi):
2025-07-01 03:01:46.022 bj = b[j]
2025-07-01 03:01:46.028 cruncher.set_seq2(bj)
2025-07-01 03:01:46.034 for i in range(alo, ahi):
2025-07-01 03:01:46.040 ai = a[i]
2025-07-01 03:01:46.046 if ai == bj:
2025-07-01 03:01:46.052 if eqi is None:
2025-07-01 03:01:46.057 eqi, eqj = i, j
2025-07-01 03:01:46.063 continue
2025-07-01 03:01:46.070 cruncher.set_seq1(ai)
2025-07-01 03:01:46.077 # computing similarity is expensive, so use the quick
2025-07-01 03:01:46.086 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:46.097 # compares by a factor of 3.
2025-07-01 03:01:46.102 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:46.107 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:46.111 # of the computation is cached by cruncher
2025-07-01 03:01:46.116 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:46.121 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:46.125 cruncher.ratio() > best_ratio:
2025-07-01 03:01:46.132 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:46.146 if best_ratio < cutoff:
2025-07-01 03:01:46.162 # no non-identical "pretty close" pair
2025-07-01 03:01:46.170 if eqi is None:
2025-07-01 03:01:46.186 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:46.194 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:46.201 return
2025-07-01 03:01:46.218 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:46.226 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:46.242 else:
2025-07-01 03:01:46.250 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:46.262 eqi = None
2025-07-01 03:01:46.274
2025-07-01 03:01:46.282 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:46.292 # identical
2025-07-01 03:01:46.306
2025-07-01 03:01:46.316 # pump out diffs from before the synch point
2025-07-01 03:01:46.330 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:46.342
2025-07-01 03:01:46.358 # do intraline marking on the synch pair
2025-07-01 03:01:46.366 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:46.374 if eqi is None:
2025-07-01 03:01:46.386 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:46.394 atags = btags = ""
2025-07-01 03:01:46.404 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:46.416 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:46.434 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:46.446 if tag == 'replace':
2025-07-01 03:01:46.457 atags += '^' * la
2025-07-01 03:01:46.474 btags += '^' * lb
2025-07-01 03:01:46.482 elif tag == 'delete':
2025-07-01 03:01:46.494 atags += '-' * la
2025-07-01 03:01:46.506 elif tag == 'insert':
2025-07-01 03:01:46.516 btags += '+' * lb
2025-07-01 03:01:46.526 elif tag == 'equal':
2025-07-01 03:01:46.538 atags += ' ' * la
2025-07-01 03:01:46.554 btags += ' ' * lb
2025-07-01 03:01:46.566 else:
2025-07-01 03:01:46.578 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:46.594 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:46.606 else:
2025-07-01 03:01:46.614 # the synch pair is identical
2025-07-01 03:01:46.630 yield '  ' + aelt
2025-07-01 03:01:46.642
2025-07-01 03:01:46.650 # pump out diffs from after the synch point
2025-07-01 03:01:46.666 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:46.678
2025-07-01 03:01:46.690 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:46.702 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:46.710
2025-07-01 03:01:46.725 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:46.734 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:46.745 alo = 175, ahi = 1101
2025-07-01 03:01:46.758 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:46.777 blo = 175, bhi = 1101
2025-07-01 03:01:46.790
2025-07-01 03:01:46.802 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:46.814 g = []
2025-07-01 03:01:46.822 if alo < ahi:
2025-07-01 03:01:46.834 if blo < bhi:
2025-07-01 03:01:46.842 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:46.857 else:
2025-07-01 03:01:46.874 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:46.882 elif blo < bhi:
2025-07-01 03:01:46.902 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:46.910
2025-07-01 03:01:46.922 >       yield from g
2025-07-01 03:01:46.933
2025-07-01 03:01:46.941 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:46.950 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:46.955
2025-07-01 03:01:46.969 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:46.983 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:46.990 alo = 175, ahi = 1101
2025-07-01 03:01:47.002 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:47.010 blo = 175, bhi = 1101
2025-07-01 03:01:47.021
2025-07-01 03:01:47.036 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:47.047 r"""
2025-07-01 03:01:47.062 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:47.078 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:47.085 synch point, and intraline difference marking is done on the
2025-07-01 03:01:47.102 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:47.114
2025-07-01 03:01:47.122 Example:
2025-07-01 03:01:47.134
2025-07-01 03:01:47.142 >>> d = Differ()
2025-07-01 03:01:47.154 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:47.162 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:47.174 >>> print(''.join(results), end="")
2025-07-01 03:01:47.186 - abcDefghiJkl
2025-07-01 03:01:47.206 + abcdefGhijkl
2025-07-01 03:01:47.234 """
2025-07-01 03:01:47.242
2025-07-01 03:01:47.254 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:47.270 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:47.282 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:47.290 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:47.302 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:47.318
2025-07-01 03:01:47.326 # search for the pair that matches best without being identical
2025-07-01 03:01:47.338 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:47.350 # on junk -- unless we have to)
2025-07-01 03:01:47.356 for j in range(blo, bhi):
2025-07-01 03:01:47.370 bj = b[j]
2025-07-01 03:01:47.378 cruncher.set_seq2(bj)
2025-07-01 03:01:47.386 for i in range(alo, ahi):
2025-07-01 03:01:47.398 ai = a[i]
2025-07-01 03:01:47.410 if ai == bj:
2025-07-01 03:01:47.422 if eqi is None:
2025-07-01 03:01:47.438 eqi, eqj = i, j
2025-07-01 03:01:47.450 continue
2025-07-01 03:01:47.466 cruncher.set_seq1(ai)
2025-07-01 03:01:47.478 # computing similarity is expensive, so use the quick
2025-07-01 03:01:47.490 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:47.505 # compares by a factor of 3.
2025-07-01 03:01:47.512 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:47.525 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:47.534 # of the computation is cached by cruncher
2025-07-01 03:01:47.542 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:47.557 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:47.566 cruncher.ratio() > best_ratio:
2025-07-01 03:01:47.578 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:47.590 if best_ratio < cutoff:
2025-07-01 03:01:47.602 # no non-identical "pretty close" pair
2025-07-01 03:01:47.614 if eqi is None:
2025-07-01 03:01:47.626 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:47.638 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:47.653 return
2025-07-01 03:01:47.662 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:47.674 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:47.686 else:
2025-07-01 03:01:47.694 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:47.706 eqi = None
2025-07-01 03:01:47.714
2025-07-01 03:01:47.726 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:47.734 # identical
2025-07-01 03:01:47.748
2025-07-01 03:01:47.758 # pump out diffs from before the synch point
2025-07-01 03:01:47.770 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:47.778
2025-07-01 03:01:47.790 # do intraline marking on the synch pair
2025-07-01 03:01:47.798 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:47.810 if eqi is None:
2025-07-01 03:01:47.822 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:47.830 atags = btags = ""
2025-07-01 03:01:47.840 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:47.851 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:47.868 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:47.882 if tag == 'replace':
2025-07-01 03:01:47.889 atags += '^' * la
2025-07-01 03:01:47.902 btags += '^' * lb
2025-07-01 03:01:47.910 elif tag == 'delete':
2025-07-01 03:01:47.922 atags += '-' * la
2025-07-01 03:01:47.934 elif tag == 'insert':
2025-07-01 03:01:47.942 btags += '+' * lb
2025-07-01 03:01:47.958 elif tag == 'equal':
2025-07-01 03:01:47.966 atags += ' ' * la
2025-07-01 03:01:47.982 btags += ' ' * lb
2025-07-01 03:01:47.990 else:
2025-07-01 03:01:47.997 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:48.014 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:48.022 else:
2025-07-01 03:01:48.030 # the synch pair is identical
2025-07-01 03:01:48.041 yield '  ' + aelt
2025-07-01 03:01:48.050
2025-07-01 03:01:48.062 # pump out diffs from after the synch point
2025-07-01 03:01:48.074 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:48.084
2025-07-01 03:01:48.094 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:48.111 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:48.118
2025-07-01 03:01:48.126 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:48.139 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:48.152 alo = 176, ahi = 1101
2025-07-01 03:01:48.170 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:48.182 blo = 176, bhi = 1101
2025-07-01 03:01:48.190
2025-07-01 03:01:48.198 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:48.209 g = []
2025-07-01 03:01:48.221 if alo < ahi:
2025-07-01 03:01:48.236 if blo < bhi:
2025-07-01 03:01:48.247 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:48.255 else:
2025-07-01 03:01:48.262 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:48.273 elif blo < bhi:
2025-07-01 03:01:48.284 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:48.294
2025-07-01 03:01:48.301 >       yield from g
2025-07-01 03:01:48.314
2025-07-01 03:01:48.322 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:48.330 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:48.334
2025-07-01 03:01:48.339 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:48.345 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:48.350 alo = 176, ahi = 1101
2025-07-01 03:01:48.362 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:48.377 blo = 176, bhi = 1101
2025-07-01 03:01:48.387
2025-07-01 03:01:48.398 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:48.414 r"""
2025-07-01 03:01:48.421 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:48.427 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:48.432 synch point, and intraline difference marking is done on the
2025-07-01 03:01:48.439 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:48.443
2025-07-01 03:01:48.449 Example:
2025-07-01 03:01:48.459
2025-07-01 03:01:48.475 >>> d = Differ()
2025-07-01 03:01:48.495 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:48.503 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:48.519 >>> print(''.join(results), end="")
2025-07-01 03:01:48.541 - abcDefghiJkl
2025-07-01 03:01:48.569 + abcdefGhijkl
2025-07-01 03:01:48.598 """
2025-07-01 03:01:48.606
2025-07-01 03:01:48.615 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:48.621 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:48.634 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:48.642 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:48.650 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:48.662
2025-07-01 03:01:48.669 # search for the pair that matches best without being identical
2025-07-01 03:01:48.682 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:48.690 # on junk -- unless we have to)
2025-07-01 03:01:48.702 for j in range(blo, bhi):
2025-07-01 03:01:48.710 bj = b[j]
2025-07-01 03:01:48.721 cruncher.set_seq2(bj)
2025-07-01 03:01:48.730 for i in range(alo, ahi):
2025-07-01 03:01:48.744 ai = a[i]
2025-07-01 03:01:48.754 if ai == bj:
2025-07-01 03:01:48.768 if eqi is None:
2025-07-01 03:01:48.782 eqi, eqj = i, j
2025-07-01 03:01:48.790 continue
2025-07-01 03:01:48.800 cruncher.set_seq1(ai)
2025-07-01 03:01:48.814 # computing similarity is expensive, so use the quick
2025-07-01 03:01:48.822 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:48.829 # compares by a factor of 3.
2025-07-01 03:01:48.838 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:48.852 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:48.862 # of the computation is cached by cruncher
2025-07-01 03:01:48.878 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:48.886 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:48.898 cruncher.ratio() > best_ratio:
2025-07-01 03:01:48.906 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:48.918 if best_ratio < cutoff:
2025-07-01 03:01:48.930 # no non-identical "pretty close" pair
2025-07-01 03:01:48.946 if eqi is None:
2025-07-01 03:01:48.954 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:48.970 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:48.977 return
2025-07-01 03:01:48.994 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:49.002 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:49.010 else:
2025-07-01 03:01:49.022 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:49.030 eqi = None
2025-07-01 03:01:49.037
2025-07-01 03:01:49.050 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:49.062 # identical
2025-07-01 03:01:49.070
2025-07-01 03:01:49.078 # pump out diffs from before the synch point
2025-07-01 03:01:49.090 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:49.098
2025-07-01 03:01:49.114 # do intraline marking on the synch pair
2025-07-01 03:01:49.126 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:49.134 if eqi is None:
2025-07-01 03:01:49.150 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:49.158 atags = btags = ""
2025-07-01 03:01:49.174 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:49.181 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:49.198 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:49.206 if tag == 'replace':
2025-07-01 03:01:49.218 atags += '^' * la
2025-07-01 03:01:49.226 btags += '^' * lb
2025-07-01 03:01:49.238 elif tag == 'delete':
2025-07-01 03:01:49.246 atags += '-' * la
2025-07-01 03:01:49.258 elif tag == 'insert':
2025-07-01 03:01:49.266 btags += '+' * lb
2025-07-01 03:01:49.278 elif tag == 'equal':
2025-07-01 03:01:49.294 atags += ' ' * la
2025-07-01 03:01:49.300 btags += ' ' * lb
2025-07-01 03:01:49.305 else:
2025-07-01 03:01:49.310 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:49.314 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:49.319 else:
2025-07-01 03:01:49.325 # the synch pair is identical
2025-07-01 03:01:49.329 yield '  ' + aelt
2025-07-01 03:01:49.334
2025-07-01 03:01:49.340 # pump out diffs from after the synch point
2025-07-01 03:01:49.345 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:49.350
2025-07-01 03:01:49.354 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:49.359 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:49.363
2025-07-01 03:01:49.368 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:49.373 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:49.378 alo = 177, ahi = 1101
2025-07-01 03:01:49.383 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:49.387 blo = 177, bhi = 1101
2025-07-01 03:01:49.392
2025-07-01 03:01:49.396 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:49.401 g = []
2025-07-01 03:01:49.405 if alo < ahi:
2025-07-01 03:01:49.409 if blo < bhi:
2025-07-01 03:01:49.414 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:49.418 else:
2025-07-01 03:01:49.423 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:49.427 elif blo < bhi:
2025-07-01 03:01:49.431 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:49.436
2025-07-01 03:01:49.440 >       yield from g
2025-07-01 03:01:49.444
2025-07-01 03:01:49.449 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:49.454 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:49.458
2025-07-01 03:01:49.462 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:49.467 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:49.472 alo = 177, ahi = 1101
2025-07-01 03:01:49.477 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:49.481 blo = 177, bhi = 1101
2025-07-01 03:01:49.485
2025-07-01 03:01:49.490 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:49.494 r"""
2025-07-01 03:01:49.498 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:49.503 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:49.507 synch point, and intraline difference marking is done on the
2025-07-01 03:01:49.512 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:49.516
2025-07-01 03:01:49.520 Example:
2025-07-01 03:01:49.524
2025-07-01 03:01:49.529 >>> d = Differ()
2025-07-01 03:01:49.533 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:49.538 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:49.542 >>> print(''.join(results), end="")
2025-07-01 03:01:49.546 - abcDefghiJkl
2025-07-01 03:01:49.556 + abcdefGhijkl
2025-07-01 03:01:49.566 """
2025-07-01 03:01:49.571
2025-07-01 03:01:49.576 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:49.581 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:49.585 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:49.589 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:49.594 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:49.598
2025-07-01 03:01:49.603 # search for the pair that matches best without being identical
2025-07-01 03:01:49.607 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:49.611 # on junk -- unless we have to)
2025-07-01 03:01:49.616 for j in range(blo, bhi):
2025-07-01 03:01:49.620 bj = b[j]
2025-07-01 03:01:49.624 cruncher.set_seq2(bj)
2025-07-01 03:01:49.629 for i in range(alo, ahi):
2025-07-01 03:01:49.633 ai = a[i]
2025-07-01 03:01:49.637 if ai == bj:
2025-07-01 03:01:49.641 if eqi is None:
2025-07-01 03:01:49.646 eqi, eqj = i, j
2025-07-01 03:01:49.650 continue
2025-07-01 03:01:49.655 cruncher.set_seq1(ai)
2025-07-01 03:01:49.660 # computing similarity is expensive, so use the quick
2025-07-01 03:01:49.664 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:49.669 # compares by a factor of 3.
2025-07-01 03:01:49.673 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:49.678 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:49.683 # of the computation is cached by cruncher
2025-07-01 03:01:49.694 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:49.705 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:49.718 cruncher.ratio() > best_ratio:
2025-07-01 03:01:49.726 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:49.738 if best_ratio < cutoff:
2025-07-01 03:01:49.750 # no non-identical "pretty close" pair
2025-07-01 03:01:49.758 if eqi is None:
2025-07-01 03:01:49.770 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:49.782 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:49.794 return
2025-07-01 03:01:49.810 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:49.818 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:49.830 else:
2025-07-01 03:01:49.838 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:49.854 eqi = None
2025-07-01 03:01:49.862
2025-07-01 03:01:49.874 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:49.882 # identical
2025-07-01 03:01:49.894
2025-07-01 03:01:49.899 # pump out diffs from before the synch point
2025-07-01 03:01:49.907 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:49.926
2025-07-01 03:01:49.932 # do intraline marking on the synch pair
2025-07-01 03:01:49.946 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:49.958 if eqi is None:
2025-07-01 03:01:49.976 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:49.987 atags = btags = ""
2025-07-01 03:01:49.999 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:50.006 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:50.022 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:50.033 if tag == 'replace':
2025-07-01 03:01:50.056 atags += '^' * la
2025-07-01 03:01:50.073 btags += '^' * lb
2025-07-01 03:01:50.086 elif tag == 'delete':
2025-07-01 03:01:50.105 atags += '-' * la
2025-07-01 03:01:50.118 elif tag == 'insert':
2025-07-01 03:01:50.130 btags += '+' * lb
2025-07-01 03:01:50.138 elif tag == 'equal':
2025-07-01 03:01:50.154 atags += ' ' * la
2025-07-01 03:01:50.166 btags += ' ' * lb
2025-07-01 03:01:50.174 else:
2025-07-01 03:01:50.186 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:50.193 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:50.206 else:
2025-07-01 03:01:50.214 # the synch pair is identical
2025-07-01 03:01:50.226 yield '  ' + aelt
2025-07-01 03:01:50.234
2025-07-01 03:01:50.255 # pump out diffs from after the synch point
2025-07-01 03:01:50.270 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:50.280
2025-07-01 03:01:50.296 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:50.306 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:50.320
2025-07-01 03:01:50.330 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:50.344 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:50.354 alo = 180, ahi = 1101
2025-07-01 03:01:50.366 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:50.378 blo = 180, bhi = 1101
2025-07-01 03:01:50.390
2025-07-01 03:01:50.398 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:50.414 g = []
2025-07-01 03:01:50.422 if alo < ahi:
2025-07-01 03:01:50.438 if blo < bhi:
2025-07-01 03:01:50.449 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:50.462 else:
2025-07-01 03:01:50.474 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:50.486 elif blo < bhi:
2025-07-01 03:01:50.494 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:50.510
2025-07-01 03:01:50.522 >       yield from g
2025-07-01 03:01:50.530
2025-07-01 03:01:50.542 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:50.558 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:50.566
2025-07-01 03:01:50.574 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:50.586 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:50.598 alo = 180, ahi = 1101
2025-07-01 03:01:50.610 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:50.622 blo = 180, bhi = 1101
2025-07-01 03:01:50.634
2025-07-01 03:01:50.642 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:50.658 r"""
2025-07-01 03:01:50.666 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:50.682 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:50.690 synch point, and intraline difference marking is done on the
2025-07-01 03:01:50.706 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:50.713
2025-07-01 03:01:50.729 Example:
2025-07-01 03:01:50.737
2025-07-01 03:01:50.752 >>> d = Differ()
2025-07-01 03:01:50.766 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:50.778 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:50.790 >>> print(''.join(results), end="")
2025-07-01 03:01:50.802 - abcDefghiJkl
2025-07-01 03:01:50.822 + abcdefGhijkl
2025-07-01 03:01:50.842 """
2025-07-01 03:01:50.853
2025-07-01 03:01:50.862 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:50.874 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:50.886 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:50.892 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:50.896 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:50.901
2025-07-01 03:01:50.905 # search for the pair that matches best without being identical
2025-07-01 03:01:50.910 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:50.915 # on junk -- unless we have to)
2025-07-01 03:01:50.919 for j in range(blo, bhi):
2025-07-01 03:01:50.924 bj = b[j]
2025-07-01 03:01:50.928 cruncher.set_seq2(bj)
2025-07-01 03:01:50.934 for i in range(alo, ahi):
2025-07-01 03:01:50.938 ai = a[i]
2025-07-01 03:01:50.943 if ai == bj:
2025-07-01 03:01:50.947 if eqi is None:
2025-07-01 03:01:50.952 eqi, eqj = i, j
2025-07-01 03:01:50.957 continue
2025-07-01 03:01:50.963 cruncher.set_seq1(ai)
2025-07-01 03:01:50.967 # computing similarity is expensive, so use the quick
2025-07-01 03:01:50.972 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:50.977 # compares by a factor of 3.
2025-07-01 03:01:50.982 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:50.987 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:50.993 # of the computation is cached by cruncher
2025-07-01 03:01:50.998 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:51.004 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:51.009 cruncher.ratio() > best_ratio:
2025-07-01 03:01:51.013 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:51.018 if best_ratio < cutoff:
2025-07-01 03:01:51.023 # no non-identical "pretty close" pair
2025-07-01 03:01:51.027 if eqi is None:
2025-07-01 03:01:51.032 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:51.037 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:51.041 return
2025-07-01 03:01:51.046 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:51.051 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:51.055 else:
2025-07-01 03:01:51.060 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:51.064 eqi = None
2025-07-01 03:01:51.069
2025-07-01 03:01:51.073 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:51.078 # identical
2025-07-01 03:01:51.082
2025-07-01 03:01:51.087 # pump out diffs from before the synch point
2025-07-01 03:01:51.092 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:51.097
2025-07-01 03:01:51.103 # do intraline marking on the synch pair
2025-07-01 03:01:51.108 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:51.112 if eqi is None:
2025-07-01 03:01:51.117 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:51.121 atags = btags = ""
2025-07-01 03:01:51.126 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:51.130 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:51.135 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:51.139 if tag == 'replace':
2025-07-01 03:01:51.144 atags += '^' * la
2025-07-01 03:01:51.149 btags += '^' * lb
2025-07-01 03:01:51.155 elif tag == 'delete':
2025-07-01 03:01:51.159 atags += '-' * la
2025-07-01 03:01:51.164 elif tag == 'insert':
2025-07-01 03:01:51.168 btags += '+' * lb
2025-07-01 03:01:51.173 elif tag == 'equal':
2025-07-01 03:01:51.177 atags += ' ' * la
2025-07-01 03:01:51.182 btags += ' ' * lb
2025-07-01 03:01:51.186 else:
2025-07-01 03:01:51.191 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:51.195 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:51.200 else:
2025-07-01 03:01:51.204 # the synch pair is identical
2025-07-01 03:01:51.209 yield '  ' + aelt
2025-07-01 03:01:51.213
2025-07-01 03:01:51.218 # pump out diffs from after the synch point
2025-07-01 03:01:51.223 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:51.227
2025-07-01 03:01:51.240 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:51.248 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:51.254
2025-07-01 03:01:51.259 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:51.266 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:51.271 alo = 181, ahi = 1101
2025-07-01 03:01:51.275 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:51.280 blo = 181, bhi = 1101
2025-07-01 03:01:51.284
2025-07-01 03:01:51.289 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:51.293 g = []
2025-07-01 03:01:51.298 if alo < ahi:
2025-07-01 03:01:51.302 if blo < bhi:
2025-07-01 03:01:51.307 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:51.311 else:
2025-07-01 03:01:51.316 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:51.320 elif blo < bhi:
2025-07-01 03:01:51.325 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:51.329
2025-07-01 03:01:51.333 >       yield from g
2025-07-01 03:01:51.338
2025-07-01 03:01:51.342 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:51.347 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:51.351
2025-07-01 03:01:51.355 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:51.360 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:51.365 alo = 181, ahi = 1101
2025-07-01 03:01:51.370 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:51.375 blo = 181, bhi = 1101
2025-07-01 03:01:51.381
2025-07-01 03:01:51.387 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:51.393 r"""
2025-07-01 03:01:51.400 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:51.407 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:51.413 synch point, and intraline difference marking is done on the
2025-07-01 03:01:51.419 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:51.425
2025-07-01 03:01:51.430 Example:
2025-07-01 03:01:51.436
2025-07-01 03:01:51.442 >>> d = Differ()
2025-07-01 03:01:51.447 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:51.452 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:51.457 >>> print(''.join(results), end="")
2025-07-01 03:01:51.461 - abcDefghiJkl
2025-07-01 03:01:51.470 + abcdefGhijkl
2025-07-01 03:01:51.480 """
2025-07-01 03:01:51.485
2025-07-01 03:01:51.489 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:51.494 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:51.499 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:51.503 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:51.508 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:51.512
2025-07-01 03:01:51.517 # search for the pair that matches best without being identical
2025-07-01 03:01:51.522 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:51.526 # on junk -- unless we have to)
2025-07-01 03:01:51.531 for j in range(blo, bhi):
2025-07-01 03:01:51.535 bj = b[j]
2025-07-01 03:01:51.540 cruncher.set_seq2(bj)
2025-07-01 03:01:51.544 for i in range(alo, ahi):
2025-07-01 03:01:51.549 ai = a[i]
2025-07-01 03:01:51.554 if ai == bj:
2025-07-01 03:01:51.558 if eqi is None:
2025-07-01 03:01:51.562 eqi, eqj = i, j
2025-07-01 03:01:51.567 continue
2025-07-01 03:01:51.571 cruncher.set_seq1(ai)
2025-07-01 03:01:51.575 # computing similarity is expensive, so use the quick
2025-07-01 03:01:51.580 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:51.585 # compares by a factor of 3.
2025-07-01 03:01:51.591 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:51.596 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:51.601 # of the computation is cached by cruncher
2025-07-01 03:01:51.605 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:51.610 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:51.616 cruncher.ratio() > best_ratio:
2025-07-01 03:01:51.620 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:51.625 if best_ratio < cutoff:
2025-07-01 03:01:51.629 # no non-identical "pretty close" pair
2025-07-01 03:01:51.634 if eqi is None:
2025-07-01 03:01:51.638 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:51.643 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:51.647 return
2025-07-01 03:01:51.652 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:51.656 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:51.662 else:
2025-07-01 03:01:51.667 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:51.671 eqi = None
2025-07-01 03:01:51.676
2025-07-01 03:01:51.681 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:51.685 # identical
2025-07-01 03:01:51.690
2025-07-01 03:01:51.698 # pump out diffs from before the synch point
2025-07-01 03:01:51.704 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:51.709
2025-07-01 03:01:51.713 # do intraline marking on the synch pair
2025-07-01 03:01:51.718 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:51.722 if eqi is None:
2025-07-01 03:01:51.728 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:51.738 atags = btags = ""
2025-07-01 03:01:51.752 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:51.761 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:51.770 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:51.781 if tag == 'replace':
2025-07-01 03:01:51.791 atags += '^' * la
2025-07-01 03:01:51.801 btags += '^' * lb
2025-07-01 03:01:51.809 elif tag == 'delete':
2025-07-01 03:01:51.824 atags += '-' * la
2025-07-01 03:01:51.837 elif tag == 'insert':
2025-07-01 03:01:51.850 btags += '+' * lb
2025-07-01 03:01:51.866 elif tag == 'equal':
2025-07-01 03:01:51.874 atags += ' ' * la
2025-07-01 03:01:51.881 btags += ' ' * lb
2025-07-01 03:01:51.887 else:
2025-07-01 03:01:51.897 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:51.910 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:51.918 else:
2025-07-01 03:01:51.930 # the synch pair is identical
2025-07-01 03:01:51.938 yield '  ' + aelt
2025-07-01 03:01:51.947
2025-07-01 03:01:51.961 # pump out diffs from after the synch point
2025-07-01 03:01:51.974 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:51.981
2025-07-01 03:01:51.994 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:52.009 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:52.026
2025-07-01 03:01:52.034 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:52.050 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:52.066 alo = 182, ahi = 1101
2025-07-01 03:01:52.077 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:52.091 blo = 182, bhi = 1101
2025-07-01 03:01:52.106
2025-07-01 03:01:52.118 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:52.130 g = []
2025-07-01 03:01:52.142 if alo < ahi:
2025-07-01 03:01:52.150 if blo < bhi:
2025-07-01 03:01:52.162 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:52.170 else:
2025-07-01 03:01:52.183 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:52.198 elif blo < bhi:
2025-07-01 03:01:52.206 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:52.218
2025-07-01 03:01:52.226 >       yield from g
2025-07-01 03:01:52.236
2025-07-01 03:01:52.246 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:52.260 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:52.270
2025-07-01 03:01:52.286 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:52.294 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:52.306 alo = 182, ahi = 1101
2025-07-01 03:01:52.314 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:52.326 blo = 182, bhi = 1101
2025-07-01 03:01:52.334
2025-07-01 03:01:52.348 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:52.368 r"""
2025-07-01 03:01:52.378 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:52.386 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:52.398 synch point, and intraline difference marking is done on the
2025-07-01 03:01:52.410 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:52.418
2025-07-01 03:01:52.430 Example:
2025-07-01 03:01:52.437
2025-07-01 03:01:52.450 >>> d = Differ()
2025-07-01 03:01:52.462 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:52.476 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:52.486 >>> print(''.join(results), end="")
2025-07-01 03:01:52.502 - abcDefghiJkl
2025-07-01 03:01:52.522 + abcdefGhijkl
2025-07-01 03:01:52.542 """
2025-07-01 03:01:52.553
2025-07-01 03:01:52.562 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:52.574 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:52.582 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:52.594 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:52.606 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:52.613
2025-07-01 03:01:52.626 # search for the pair that matches best without being identical
2025-07-01 03:01:52.640 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:52.652 # on junk -- unless we have to)
2025-07-01 03:01:52.662 for j in range(blo, bhi):
2025-07-01 03:01:52.674 bj = b[j]
2025-07-01 03:01:52.684 cruncher.set_seq2(bj)
2025-07-01 03:01:52.699 for i in range(alo, ahi):
2025-07-01 03:01:52.718 ai = a[i]
2025-07-01 03:01:52.726 if ai == bj:
2025-07-01 03:01:52.742 if eqi is None:
2025-07-01 03:01:52.758 eqi, eqj = i, j
2025-07-01 03:01:52.770 continue
2025-07-01 03:01:52.776 cruncher.set_seq1(ai)
2025-07-01 03:01:52.794 # computing similarity is expensive, so use the quick
2025-07-01 03:01:52.808 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:52.826 # compares by a factor of 3.
2025-07-01 03:01:52.834 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:52.846 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:52.854 # of the computation is cached by cruncher
2025-07-01 03:01:52.866 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:52.877 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:52.886 cruncher.ratio() > best_ratio:
2025-07-01 03:01:52.894 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:52.906 if best_ratio < cutoff:
2025-07-01 03:01:52.914 # no non-identical "pretty close" pair
2025-07-01 03:01:52.926 if eqi is None:
2025-07-01 03:01:52.934 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:52.946 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:52.954 return
2025-07-01 03:01:52.968 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:52.978 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:52.988 else:
2025-07-01 03:01:52.998 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:53.009 eqi = None
2025-07-01 03:01:53.018
2025-07-01 03:01:53.034 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:53.042 # identical
2025-07-01 03:01:53.058
2025-07-01 03:01:53.066 # pump out diffs from before the synch point
2025-07-01 03:01:53.081 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:53.089
2025-07-01 03:01:53.102 # do intraline marking on the synch pair
2025-07-01 03:01:53.114 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:53.126 if eqi is None:
2025-07-01 03:01:53.137 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:53.154 atags = btags = ""
2025-07-01 03:01:53.168 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:53.178 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:53.190 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:53.202 if tag == 'replace':
2025-07-01 03:01:53.210 atags += '^' * la
2025-07-01 03:01:53.222 btags += '^' * lb
2025-07-01 03:01:53.234 elif tag == 'delete':
2025-07-01 03:01:53.241 atags += '-' * la
2025-07-01 03:01:53.250 elif tag == 'insert':
2025-07-01 03:01:53.266 btags += '+' * lb
2025-07-01 03:01:53.273 elif tag == 'equal':
2025-07-01 03:01:53.290 atags += ' ' * la
2025-07-01 03:01:53.298 btags += ' ' * lb
2025-07-01 03:01:53.306 else:
2025-07-01 03:01:53.322 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:53.330 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:53.342 else:
2025-07-01 03:01:53.358 # the synch pair is identical
2025-07-01 03:01:53.366 yield '  ' + aelt
2025-07-01 03:01:53.376
2025-07-01 03:01:53.388 # pump out diffs from after the synch point
2025-07-01 03:01:53.402 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:53.416
2025-07-01 03:01:53.434 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:53.447 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:53.465
2025-07-01 03:01:53.481 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:53.497 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:53.506 alo = 183, ahi = 1101
2025-07-01 03:01:53.521 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:53.541 blo = 183, bhi = 1101
2025-07-01 03:01:53.557
2025-07-01 03:01:53.566 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:53.576 g = []
2025-07-01 03:01:53.590 if alo < ahi:
2025-07-01 03:01:53.598 if blo < bhi:
2025-07-01 03:01:53.610 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:53.621 else:
2025-07-01 03:01:53.630 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:53.642 elif blo < bhi:
2025-07-01 03:01:53.650 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:53.662
2025-07-01 03:01:53.669 >       yield from g
2025-07-01 03:01:53.682
2025-07-01 03:01:53.690 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:53.702 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:53.710
2025-07-01 03:01:53.722 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:53.734 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:53.742 alo = 183, ahi = 1101
2025-07-01 03:01:53.754 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:53.766 blo = 183, bhi = 1101
2025-07-01 03:01:53.774
2025-07-01 03:01:53.786 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:53.796 r"""
2025-07-01 03:01:53.810 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:53.818 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:53.830 synch point, and intraline difference marking is done on the
2025-07-01 03:01:53.838 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:53.848
2025-07-01 03:01:53.857 Example:
2025-07-01 03:01:53.868
2025-07-01 03:01:53.878 >>> d = Differ()
2025-07-01 03:01:53.890 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:53.898 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:53.910 >>> print(''.join(results), end="")
2025-07-01 03:01:53.922 - abcDefghiJkl
2025-07-01 03:01:53.938 + abcdefGhijkl
2025-07-01 03:01:53.958 """
2025-07-01 03:01:53.970
2025-07-01 03:01:53.986 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:53.994 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:54.002 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:54.014 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:54.030 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:54.040
2025-07-01 03:01:54.045 # search for the pair that matches best without being identical
2025-07-01 03:01:54.050 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:54.055 # on junk -- unless we have to)
2025-07-01 03:01:54.060 for j in range(blo, bhi):
2025-07-01 03:01:54.065 bj = b[j]
2025-07-01 03:01:54.070 cruncher.set_seq2(bj)
2025-07-01 03:01:54.075 for i in range(alo, ahi):
2025-07-01 03:01:54.080 ai = a[i]
2025-07-01 03:01:54.084 if ai == bj:
2025-07-01 03:01:54.089 if eqi is None:
2025-07-01 03:01:54.094 eqi, eqj = i, j
2025-07-01 03:01:54.098 continue
2025-07-01 03:01:54.104 cruncher.set_seq1(ai)
2025-07-01 03:01:54.109 # computing similarity is expensive, so use the quick
2025-07-01 03:01:54.113 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:54.118 # compares by a factor of 3.
2025-07-01 03:01:54.123 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:54.128 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:54.133 # of the computation is cached by cruncher
2025-07-01 03:01:54.138 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:54.143 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:54.148 cruncher.ratio() > best_ratio:
2025-07-01 03:01:54.152 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:54.157 if best_ratio < cutoff:
2025-07-01 03:01:54.161 # no non-identical "pretty close" pair
2025-07-01 03:01:54.166 if eqi is None:
2025-07-01 03:01:54.170 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:54.175 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:54.181 return
2025-07-01 03:01:54.186 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:54.190 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:54.195 else:
2025-07-01 03:01:54.199 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:54.204 eqi = None
2025-07-01 03:01:54.208
2025-07-01 03:01:54.213 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:54.217 # identical
2025-07-01 03:01:54.222
2025-07-01 03:01:54.227 # pump out diffs from before the synch point
2025-07-01 03:01:54.232 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:54.236
2025-07-01 03:01:54.241 # do intraline marking on the synch pair
2025-07-01 03:01:54.245 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:54.250 if eqi is None:
2025-07-01 03:01:54.255 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:54.259 atags = btags = ""
2025-07-01 03:01:54.265 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:54.269 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:54.274 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:54.279 if tag == 'replace':
2025-07-01 03:01:54.283 atags += '^' * la
2025-07-01 03:01:54.288 btags += '^' * lb
2025-07-01 03:01:54.292 elif tag == 'delete':
2025-07-01 03:01:54.298 atags += '-' * la
2025-07-01 03:01:54.303 elif tag == 'insert':
2025-07-01 03:01:54.308 btags += '+' * lb
2025-07-01 03:01:54.313 elif tag == 'equal':
2025-07-01 03:01:54.317 atags += ' ' * la
2025-07-01 03:01:54.322 btags += ' ' * lb
2025-07-01 03:01:54.327 else:
2025-07-01 03:01:54.332 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:54.336 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:54.341 else:
2025-07-01 03:01:54.345 # the synch pair is identical
2025-07-01 03:01:54.350 yield '  ' + aelt
2025-07-01 03:01:54.355
2025-07-01 03:01:54.359 # pump out diffs from after the synch point
2025-07-01 03:01:54.364 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:54.369
2025-07-01 03:01:54.373 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:54.378 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:54.382
2025-07-01 03:01:54.387 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:54.393 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:54.397 alo = 184, ahi = 1101
2025-07-01 03:01:54.403 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:54.409 blo = 184, bhi = 1101
2025-07-01 03:01:54.416
2025-07-01 03:01:54.422 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:54.429 g = []
2025-07-01 03:01:54.436 if alo < ahi:
2025-07-01 03:01:54.443 if blo < bhi:
2025-07-01 03:01:54.450 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:54.456 else:
2025-07-01 03:01:54.461 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:54.465 elif blo < bhi:
2025-07-01 03:01:54.470 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:54.474
2025-07-01 03:01:54.479 >       yield from g
2025-07-01 03:01:54.483
2025-07-01 03:01:54.488 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:54.493 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:54.497
2025-07-01 03:01:54.502 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:54.507 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:54.512 alo = 184, ahi = 1101
2025-07-01 03:01:54.517 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:54.522 blo = 184, bhi = 1101
2025-07-01 03:01:54.526
2025-07-01 03:01:54.532 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:54.536 r"""
2025-07-01 03:01:54.541 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:54.546 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:54.551 synch point, and intraline difference marking is done on the
2025-07-01 03:01:54.555 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:54.560
2025-07-01 03:01:54.564 Example:
2025-07-01 03:01:54.569
2025-07-01 03:01:54.574 >>> d = Differ()
2025-07-01 03:01:54.579 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:54.583 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:54.589 >>> print(''.join(results), end="")
2025-07-01 03:01:54.593 - abcDefghiJkl
2025-07-01 03:01:54.603 + abcdefGhijkl
2025-07-01 03:01:54.614 """
2025-07-01 03:01:54.618
2025-07-01 03:01:54.623 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:54.628 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:54.633 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:54.638 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:54.643 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:54.647
2025-07-01 03:01:54.652 # search for the pair that matches best without being identical
2025-07-01 03:01:54.657 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:54.661 # on junk -- unless we have to)
2025-07-01 03:01:54.666 for j in range(blo, bhi):
2025-07-01 03:01:54.671 bj = b[j]
2025-07-01 03:01:54.675 cruncher.set_seq2(bj)
2025-07-01 03:01:54.680 for i in range(alo, ahi):
2025-07-01 03:01:54.685 ai = a[i]
2025-07-01 03:01:54.690 if ai == bj:
2025-07-01 03:01:54.695 if eqi is None:
2025-07-01 03:01:54.700 eqi, eqj = i, j
2025-07-01 03:01:54.704 continue
2025-07-01 03:01:54.709 cruncher.set_seq1(ai)
2025-07-01 03:01:54.714 # computing similarity is expensive, so use the quick
2025-07-01 03:01:54.718 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:54.723 # compares by a factor of 3.
2025-07-01 03:01:54.729 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:54.734 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:54.739 # of the computation is cached by cruncher
2025-07-01 03:01:54.744 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:54.749 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:54.754 cruncher.ratio() > best_ratio:
2025-07-01 03:01:54.759 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:54.763 if best_ratio < cutoff:
2025-07-01 03:01:54.768 # no non-identical "pretty close" pair
2025-07-01 03:01:54.774 if eqi is None:
2025-07-01 03:01:54.779 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:54.784 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:54.789 return
2025-07-01 03:01:54.794 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:54.799 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:54.803 else:
2025-07-01 03:01:54.808 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:54.813 eqi = None
2025-07-01 03:01:54.818
2025-07-01 03:01:54.823 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:54.828 # identical
2025-07-01 03:01:54.833
2025-07-01 03:01:54.838 # pump out diffs from before the synch point
2025-07-01 03:01:54.843 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:54.847
2025-07-01 03:01:54.852 # do intraline marking on the synch pair
2025-07-01 03:01:54.856 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:54.861 if eqi is None:
2025-07-01 03:01:54.865 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:54.870 atags = btags = ""
2025-07-01 03:01:54.877 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:54.890 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:54.902 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:54.914 if tag == 'replace':
2025-07-01 03:01:54.922 atags += '^' * la
2025-07-01 03:01:54.937 btags += '^' * lb
2025-07-01 03:01:54.953 elif tag == 'delete':
2025-07-01 03:01:54.966 atags += '-' * la
2025-07-01 03:01:54.982 elif tag == 'insert':
2025-07-01 03:01:54.990 btags += '+' * lb
2025-07-01 03:01:55.003 elif tag == 'equal':
2025-07-01 03:01:55.012 atags += ' ' * la
2025-07-01 03:01:55.026 btags += ' ' * lb
2025-07-01 03:01:55.042 else:
2025-07-01 03:01:55.050 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:55.062 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:55.077 else:
2025-07-01 03:01:55.090 # the synch pair is identical
2025-07-01 03:01:55.106 yield '  ' + aelt
2025-07-01 03:01:55.122
2025-07-01 03:01:55.130 # pump out diffs from after the synch point
2025-07-01 03:01:55.142 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:55.148
2025-07-01 03:01:55.153 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:55.158 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:55.163
2025-07-01 03:01:55.167 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:55.174 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:55.179 alo = 185, ahi = 1101
2025-07-01 03:01:55.184 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:55.189 blo = 185, bhi = 1101
2025-07-01 03:01:55.193
2025-07-01 03:01:55.199 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:55.204 g = []
2025-07-01 03:01:55.209 if alo < ahi:
2025-07-01 03:01:55.213 if blo < bhi:
2025-07-01 03:01:55.219 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:55.223 else:
2025-07-01 03:01:55.228 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:55.232 elif blo < bhi:
2025-07-01 03:01:55.237 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:55.242
2025-07-01 03:01:55.251 >       yield from g
2025-07-01 03:01:55.263
2025-07-01 03:01:55.273 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:55.286 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:55.305
2025-07-01 03:01:55.314 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:55.326 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:55.333 alo = 185, ahi = 1101
2025-07-01 03:01:55.350 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:55.362 blo = 185, bhi = 1101
2025-07-01 03:01:55.370
2025-07-01 03:01:55.377 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:55.383 r"""
2025-07-01 03:01:55.387 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:55.392 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:55.396 synch point, and intraline difference marking is done on the
2025-07-01 03:01:55.401 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:55.405
2025-07-01 03:01:55.409 Example:
2025-07-01 03:01:55.413
2025-07-01 03:01:55.418 >>> d = Differ()
2025-07-01 03:01:55.422 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:55.426 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:55.431 >>> print(''.join(results), end="")
2025-07-01 03:01:55.435 - abcDefghiJkl
2025-07-01 03:01:55.444 + abcdefGhijkl
2025-07-01 03:01:55.452 """
2025-07-01 03:01:55.457
2025-07-01 03:01:55.461 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:55.465 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:55.470 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:55.474 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:55.479 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:55.483
2025-07-01 03:01:55.487 # search for the pair that matches best without being identical
2025-07-01 03:01:55.492 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:55.496 # on junk -- unless we have to)
2025-07-01 03:01:55.500 for j in range(blo, bhi):
2025-07-01 03:01:55.505 bj = b[j]
2025-07-01 03:01:55.509 cruncher.set_seq2(bj)
2025-07-01 03:01:55.516 for i in range(alo, ahi):
2025-07-01 03:01:55.525 ai = a[i]
2025-07-01 03:01:55.540 if ai == bj:
2025-07-01 03:01:55.552 if eqi is None:
2025-07-01 03:01:55.562 eqi, eqj = i, j
2025-07-01 03:01:55.575 continue
2025-07-01 03:01:55.583 cruncher.set_seq1(ai)
2025-07-01 03:01:55.598 # computing similarity is expensive, so use the quick
2025-07-01 03:01:55.605 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:55.622 # compares by a factor of 3.
2025-07-01 03:01:55.632 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:55.650 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:55.666 # of the computation is cached by cruncher
2025-07-01 03:01:55.676 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:55.686 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:55.694 cruncher.ratio() > best_ratio:
2025-07-01 03:01:55.706 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:55.714 if best_ratio < cutoff:
2025-07-01 03:01:55.724 # no non-identical "pretty close" pair
2025-07-01 03:01:55.742 if eqi is None:
2025-07-01 03:01:55.750 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:55.762 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:55.770 return
2025-07-01 03:01:55.782 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:55.794 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:55.806 else:
2025-07-01 03:01:55.816 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:55.829 eqi = None
2025-07-01 03:01:55.842
2025-07-01 03:01:55.850 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:55.862 # identical
2025-07-01 03:01:55.870
2025-07-01 03:01:55.880 # pump out diffs from before the synch point
2025-07-01 03:01:55.890 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:55.902
2025-07-01 03:01:55.910 # do intraline marking on the synch pair
2025-07-01 03:01:55.922 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:55.930 if eqi is None:
2025-07-01 03:01:55.942 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:55.950 atags = btags = ""
2025-07-01 03:01:55.960 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:55.977 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:55.990 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:55.998 if tag == 'replace':
2025-07-01 03:01:56.014 atags += '^' * la
2025-07-01 03:01:56.026 btags += '^' * lb
2025-07-01 03:01:56.035 elif tag == 'delete':
2025-07-01 03:01:56.044 atags += '-' * la
2025-07-01 03:01:56.058 elif tag == 'insert':
2025-07-01 03:01:56.068 btags += '+' * lb
2025-07-01 03:01:56.078 elif tag == 'equal':
2025-07-01 03:01:56.094 atags += ' ' * la
2025-07-01 03:01:56.102 btags += ' ' * lb
2025-07-01 03:01:56.110 else:
2025-07-01 03:01:56.122 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:56.138 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:56.150 else:
2025-07-01 03:01:56.162 # the synch pair is identical
2025-07-01 03:01:56.178 yield '  ' + aelt
2025-07-01 03:01:56.186
2025-07-01 03:01:56.194 # pump out diffs from after the synch point
2025-07-01 03:01:56.202 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:56.216
2025-07-01 03:01:56.226 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:56.238 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:56.245
2025-07-01 03:01:56.258 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:56.266 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:56.280 alo = 186, ahi = 1101
2025-07-01 03:01:56.294 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:56.306 blo = 186, bhi = 1101
2025-07-01 03:01:56.318
2025-07-01 03:01:56.330 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:56.338 g = []
2025-07-01 03:01:56.348 if alo < ahi:
2025-07-01 03:01:56.358 if blo < bhi:
2025-07-01 03:01:56.370 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:56.381 else:
2025-07-01 03:01:56.394 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:56.400 elif blo < bhi:
2025-07-01 03:01:56.414 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:56.426
2025-07-01 03:01:56.434 >       yield from g
2025-07-01 03:01:56.446
2025-07-01 03:01:56.458 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:56.466 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:56.478
2025-07-01 03:01:56.486 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:56.498 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:56.514 alo = 186, ahi = 1101
2025-07-01 03:01:56.530 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:56.538 blo = 186, bhi = 1101
2025-07-01 03:01:56.549
2025-07-01 03:01:56.562 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:56.574 r"""
2025-07-01 03:01:56.586 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:56.593 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:56.605 synch point, and intraline difference marking is done on the
2025-07-01 03:01:56.622 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:56.638
2025-07-01 03:01:56.644 Example:
2025-07-01 03:01:56.658
2025-07-01 03:01:56.670 >>> d = Differ()
2025-07-01 03:01:56.682 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:56.694 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:56.702 >>> print(''.join(results), end="")
2025-07-01 03:01:56.718 - abcDefghiJkl
2025-07-01 03:01:56.738 + abcdefGhijkl
2025-07-01 03:01:56.760 """
2025-07-01 03:01:56.770
2025-07-01 03:01:56.782 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:56.790 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:56.800 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:56.814 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:56.830 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:56.837
2025-07-01 03:01:56.844 # search for the pair that matches best without being identical
2025-07-01 03:01:56.855 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:56.870 # on junk -- unless we have to)
2025-07-01 03:01:56.882 for j in range(blo, bhi):
2025-07-01 03:01:56.893 bj = b[j]
2025-07-01 03:01:56.901 cruncher.set_seq2(bj)
2025-07-01 03:01:56.914 for i in range(alo, ahi):
2025-07-01 03:01:56.922 ai = a[i]
2025-07-01 03:01:56.929 if ai == bj:
2025-07-01 03:01:56.944 if eqi is None:
2025-07-01 03:01:56.954 eqi, eqj = i, j
2025-07-01 03:01:56.966 continue
2025-07-01 03:01:56.978 cruncher.set_seq1(ai)
2025-07-01 03:01:56.990 # computing similarity is expensive, so use the quick
2025-07-01 03:01:57.002 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:57.010 # compares by a factor of 3.
2025-07-01 03:01:57.022 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:57.030 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:57.042 # of the computation is cached by cruncher
2025-07-01 03:01:57.050 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:57.062 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:57.074 cruncher.ratio() > best_ratio:
2025-07-01 03:01:57.086 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:57.094 if best_ratio < cutoff:
2025-07-01 03:01:57.108 # no non-identical "pretty close" pair
2025-07-01 03:01:57.118 if eqi is None:
2025-07-01 03:01:57.130 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:57.137 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:57.145 return
2025-07-01 03:01:57.159 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:57.174 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:57.186 else:
2025-07-01 03:01:57.198 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:57.206 eqi = None
2025-07-01 03:01:57.218
2025-07-01 03:01:57.230 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:57.244 # identical
2025-07-01 03:01:57.258
2025-07-01 03:01:57.270 # pump out diffs from before the synch point
2025-07-01 03:01:57.282 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:57.294
2025-07-01 03:01:57.310 # do intraline marking on the synch pair
2025-07-01 03:01:57.318 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:57.338 if eqi is None:
2025-07-01 03:01:57.346 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:57.354 atags = btags = ""
2025-07-01 03:01:57.366 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:57.374 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:57.386 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:57.398 if tag == 'replace':
2025-07-01 03:01:57.406 atags += '^' * la
2025-07-01 03:01:57.418 btags += '^' * lb
2025-07-01 03:01:57.428 elif tag == 'delete':
2025-07-01 03:01:57.442 atags += '-' * la
2025-07-01 03:01:57.454 elif tag == 'insert':
2025-07-01 03:01:57.462 btags += '+' * lb
2025-07-01 03:01:57.478 elif tag == 'equal':
2025-07-01 03:01:57.491 atags += ' ' * la
2025-07-01 03:01:57.504 btags += ' ' * lb
2025-07-01 03:01:57.518 else:
2025-07-01 03:01:57.529 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:57.537 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:57.546 else:
2025-07-01 03:01:57.555 # the synch pair is identical
2025-07-01 03:01:57.575 yield '  ' + aelt
2025-07-01 03:01:57.588
2025-07-01 03:01:57.605 # pump out diffs from after the synch point
2025-07-01 03:01:57.615 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:57.625
2025-07-01 03:01:57.634 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:57.649 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:57.665
2025-07-01 03:01:57.674 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:57.690 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:57.702 alo = 187, ahi = 1101
2025-07-01 03:01:57.714 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:57.722 blo = 187, bhi = 1101
2025-07-01 03:01:57.734
2025-07-01 03:01:57.745 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:57.758 g = []
2025-07-01 03:01:57.766 if alo < ahi:
2025-07-01 03:01:57.772 if blo < bhi:
2025-07-01 03:01:57.790 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:57.798 else:
2025-07-01 03:01:57.806 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:57.818 elif blo < bhi:
2025-07-01 03:01:57.826 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:57.838
2025-07-01 03:01:57.853 >       yield from g
2025-07-01 03:01:57.870
2025-07-01 03:01:57.877 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:57.886 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:57.892
2025-07-01 03:01:57.896 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:57.902 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:57.906 alo = 187, ahi = 1101
2025-07-01 03:01:57.912 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:57.918 blo = 187, bhi = 1101
2025-07-01 03:01:57.926
2025-07-01 03:01:57.942 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:57.955 r"""
2025-07-01 03:01:57.962 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:57.973 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:57.987 synch point, and intraline difference marking is done on the
2025-07-01 03:01:57.995 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:58.009
2025-07-01 03:01:58.019 Example:
2025-07-01 03:01:58.029
2025-07-01 03:01:58.045 >>> d = Differ()
2025-07-01 03:01:58.058 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:58.077 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:58.090 >>> print(''.join(results), end="")
2025-07-01 03:01:58.109 - abcDefghiJkl
2025-07-01 03:01:58.130 + abcdefGhijkl
2025-07-01 03:01:58.155 """
2025-07-01 03:01:58.166
2025-07-01 03:01:58.178 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:58.190 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:58.198 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:58.214 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:58.226 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:58.238
2025-07-01 03:01:58.254 # search for the pair that matches best without being identical
2025-07-01 03:01:58.262 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:58.273 # on junk -- unless we have to)
2025-07-01 03:01:58.286 for j in range(blo, bhi):
2025-07-01 03:01:58.298 bj = b[j]
2025-07-01 03:01:58.306 cruncher.set_seq2(bj)
2025-07-01 03:01:58.318 for i in range(alo, ahi):
2025-07-01 03:01:58.330 ai = a[i]
2025-07-01 03:01:58.338 if ai == bj:
2025-07-01 03:01:58.350 if eqi is None:
2025-07-01 03:01:58.366 eqi, eqj = i, j
2025-07-01 03:01:58.378 continue
2025-07-01 03:01:58.390 cruncher.set_seq1(ai)
2025-07-01 03:01:58.406 # computing similarity is expensive, so use the quick
2025-07-01 03:01:58.414 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:58.422 # compares by a factor of 3.
2025-07-01 03:01:58.434 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:58.450 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:58.466 # of the computation is cached by cruncher
2025-07-01 03:01:58.476 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:58.490 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:58.498 cruncher.ratio() > best_ratio:
2025-07-01 03:01:58.510 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:58.530 if best_ratio < cutoff:
2025-07-01 03:01:58.538 # no non-identical "pretty close" pair
2025-07-01 03:01:58.550 if eqi is None:
2025-07-01 03:01:58.563 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:58.579 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:58.591 return
2025-07-01 03:01:58.609 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:58.625 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:58.643 else:
2025-07-01 03:01:58.657 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:58.669 eqi = None
2025-07-01 03:01:58.678
2025-07-01 03:01:58.689 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:58.706 # identical
2025-07-01 03:01:58.719
2025-07-01 03:01:58.738 # pump out diffs from before the synch point
2025-07-01 03:01:58.746 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:58.762
2025-07-01 03:01:58.774 # do intraline marking on the synch pair
2025-07-01 03:01:58.785 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:58.802 if eqi is None:
2025-07-01 03:01:58.817 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:58.830 atags = btags = ""
2025-07-01 03:01:58.842 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:58.858 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:58.866 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:58.878 if tag == 'replace':
2025-07-01 03:01:58.894 atags += '^' * la
2025-07-01 03:01:58.902 btags += '^' * lb
2025-07-01 03:01:58.914 elif tag == 'delete':
2025-07-01 03:01:58.926 atags += '-' * la
2025-07-01 03:01:58.934 elif tag == 'insert':
2025-07-01 03:01:58.946 btags += '+' * lb
2025-07-01 03:01:58.954 elif tag == 'equal':
2025-07-01 03:01:58.966 atags += ' ' * la
2025-07-01 03:01:58.982 btags += ' ' * lb
2025-07-01 03:01:58.990 else:
2025-07-01 03:01:58.998 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:01:59.010 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:01:59.022 else:
2025-07-01 03:01:59.034 # the synch pair is identical
2025-07-01 03:01:59.042 yield '  ' + aelt
2025-07-01 03:01:59.054
2025-07-01 03:01:59.062 # pump out diffs from after the synch point
2025-07-01 03:01:59.074 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:01:59.082
2025-07-01 03:01:59.092 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:01:59.102 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:59.117
2025-07-01 03:01:59.126 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:59.138 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:59.149 alo = 188, ahi = 1101
2025-07-01 03:01:59.166 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:59.178 blo = 188, bhi = 1101
2025-07-01 03:01:59.190
2025-07-01 03:01:59.198 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:59.214 g = []
2025-07-01 03:01:59.232 if alo < ahi:
2025-07-01 03:01:59.246 if blo < bhi:
2025-07-01 03:01:59.258 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:59.274 else:
2025-07-01 03:01:59.290 g = self._dump('-', a, alo, ahi)
2025-07-01 03:01:59.302 elif blo < bhi:
2025-07-01 03:01:59.312 g = self._dump('+', b, blo, bhi)
2025-07-01 03:01:59.321
2025-07-01 03:01:59.331 >       yield from g
2025-07-01 03:01:59.342
2025-07-01 03:01:59.355 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:01:59.366 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:01:59.380
2025-07-01 03:01:59.400 self = <difflib.Differ object at [hex]>
2025-07-01 03:01:59.420 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:01:59.434 alo = 188, ahi = 1101
2025-07-01 03:01:59.445 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:01:59.457 blo = 188, bhi = 1101
2025-07-01 03:01:59.474
2025-07-01 03:01:59.485 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:01:59.501 r"""
2025-07-01 03:01:59.514 When replacing one block of lines with another, search the blocks
2025-07-01 03:01:59.528 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:01:59.538 synch point, and intraline difference marking is done on the
2025-07-01 03:01:59.553 similar pair. Lots of work, but often worth it.
2025-07-01 03:01:59.566
2025-07-01 03:01:59.574 Example:
2025-07-01 03:01:59.582
2025-07-01 03:01:59.593 >>> d = Differ()
2025-07-01 03:01:59.604 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:01:59.615 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:01:59.625 >>> print(''.join(results), end="")
2025-07-01 03:01:59.636 - abcDefghiJkl
2025-07-01 03:01:59.658 + abcdefGhijkl
2025-07-01 03:01:59.680 """
2025-07-01 03:01:59.690
2025-07-01 03:01:59.697 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:01:59.710 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:01:59.716 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:01:59.721 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:01:59.725 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:01:59.730
2025-07-01 03:01:59.735 # search for the pair that matches best without being identical
2025-07-01 03:01:59.740 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:01:59.745 # on junk -- unless we have to)
2025-07-01 03:01:59.749 for j in range(blo, bhi):
2025-07-01 03:01:59.754 bj = b[j]
2025-07-01 03:01:59.758 cruncher.set_seq2(bj)
2025-07-01 03:01:59.763 for i in range(alo, ahi):
2025-07-01 03:01:59.767 ai = a[i]
2025-07-01 03:01:59.771 if ai == bj:
2025-07-01 03:01:59.776 if eqi is None:
2025-07-01 03:01:59.781 eqi, eqj = i, j
2025-07-01 03:01:59.785 continue
2025-07-01 03:01:59.790 cruncher.set_seq1(ai)
2025-07-01 03:01:59.795 # computing similarity is expensive, so use the quick
2025-07-01 03:01:59.799 # upper bounds first -- have seen this speed up messy
2025-07-01 03:01:59.804 # compares by a factor of 3.
2025-07-01 03:01:59.809 # note that ratio() is only expensive to compute the first
2025-07-01 03:01:59.813 # time it's called on a sequence pair; the expensive part
2025-07-01 03:01:59.818 # of the computation is cached by cruncher
2025-07-01 03:01:59.822 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:01:59.828 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:01:59.832 cruncher.ratio() > best_ratio:
2025-07-01 03:01:59.837 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:01:59.841 if best_ratio < cutoff:
2025-07-01 03:01:59.846 # no non-identical "pretty close" pair
2025-07-01 03:01:59.850 if eqi is None:
2025-07-01 03:01:59.855 # no identical pair either -- treat it as a straight replace
2025-07-01 03:01:59.859 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:01:59.863 return
2025-07-01 03:01:59.868 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:01:59.873 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:01:59.877 else:
2025-07-01 03:01:59.881 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:01:59.886 eqi = None
2025-07-01 03:01:59.890
2025-07-01 03:01:59.894 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:01:59.899 # identical
2025-07-01 03:01:59.903
2025-07-01 03:01:59.907 # pump out diffs from before the synch point
2025-07-01 03:01:59.912 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:01:59.916
2025-07-01 03:01:59.920 # do intraline marking on the synch pair
2025-07-01 03:01:59.924 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:01:59.929 if eqi is None:
2025-07-01 03:01:59.933 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:01:59.938 atags = btags = ""
2025-07-01 03:01:59.942 cruncher.set_seqs(aelt, belt)
2025-07-01 03:01:59.946 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:01:59.951 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:01:59.955 if tag == 'replace':
2025-07-01 03:01:59.959 atags += '^' * la
2025-07-01 03:01:59.964 btags += '^' * lb
2025-07-01 03:01:59.969 elif tag == 'delete':
2025-07-01 03:01:59.974 atags += '-' * la
2025-07-01 03:01:59.978 elif tag == 'insert':
2025-07-01 03:01:59.982 btags += '+' * lb
2025-07-01 03:01:59.987 elif tag == 'equal':
2025-07-01 03:01:59.991 atags += ' ' * la
2025-07-01 03:01:59.995 btags += ' ' * lb
2025-07-01 03:02:00.000 else:
2025-07-01 03:02:00.005 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:00.012 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:00.017 else:
2025-07-01 03:02:00.021 # the synch pair is identical
2025-07-01 03:02:00.026 yield '  ' + aelt
2025-07-01 03:02:00.031
2025-07-01 03:02:00.036 # pump out diffs from after the synch point
2025-07-01 03:02:00.042 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:00.047
2025-07-01 03:02:00.052 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:00.057 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:00.063
2025-07-01 03:02:00.070 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:00.076 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:00.082 alo = 189, ahi = 1101
2025-07-01 03:02:00.087 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:00.093 blo = 189, bhi = 1101
2025-07-01 03:02:00.099
2025-07-01 03:02:00.104 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:00.109 g = []
2025-07-01 03:02:00.116 if alo < ahi:
2025-07-01 03:02:00.121 if blo < bhi:
2025-07-01 03:02:00.126 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:00.131 else:
2025-07-01 03:02:00.136 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:00.141 elif blo < bhi:
2025-07-01 03:02:00.146 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:00.150
2025-07-01 03:02:00.156 >       yield from g
2025-07-01 03:02:00.161
2025-07-01 03:02:00.166 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:00.172 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:00.177
2025-07-01 03:02:00.182 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:00.188 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:00.194 alo = 189, ahi = 1101
2025-07-01 03:02:00.200 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:00.205 blo = 189, bhi = 1101
2025-07-01 03:02:00.210
2025-07-01 03:02:00.216 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:00.222 r"""
2025-07-01 03:02:00.230 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:00.237 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:00.244 synch point, and intraline difference marking is done on the
2025-07-01 03:02:00.250 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:00.256
2025-07-01 03:02:00.263 Example:
2025-07-01 03:02:00.269
2025-07-01 03:02:00.274 >>> d = Differ()
2025-07-01 03:02:00.279 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:00.283 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:00.288 >>> print(''.join(results), end="")
2025-07-01 03:02:00.293 - abcDefghiJkl
2025-07-01 03:02:00.302 + abcdefGhijkl
2025-07-01 03:02:00.311 """
2025-07-01 03:02:00.315
2025-07-01 03:02:00.320 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:00.324 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:00.329 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:00.333 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:00.338 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:00.342
2025-07-01 03:02:00.347 # search for the pair that matches best without being identical
2025-07-01 03:02:00.351 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:00.356 # on junk -- unless we have to)
2025-07-01 03:02:00.360 for j in range(blo, bhi):
2025-07-01 03:02:00.365 bj = b[j]
2025-07-01 03:02:00.370 cruncher.set_seq2(bj)
2025-07-01 03:02:00.374 for i in range(alo, ahi):
2025-07-01 03:02:00.379 ai = a[i]
2025-07-01 03:02:00.384 if ai == bj:
2025-07-01 03:02:00.389 if eqi is None:
2025-07-01 03:02:00.394 eqi, eqj = i, j
2025-07-01 03:02:00.399 continue
2025-07-01 03:02:00.404 cruncher.set_seq1(ai)
2025-07-01 03:02:00.409 # computing similarity is expensive, so use the quick
2025-07-01 03:02:00.414 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:00.419 # compares by a factor of 3.
2025-07-01 03:02:00.423 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:00.428 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:00.433 # of the computation is cached by cruncher
2025-07-01 03:02:00.438 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:00.443 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:00.448 cruncher.ratio() > best_ratio:
2025-07-01 03:02:00.453 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:00.458 if best_ratio < cutoff:
2025-07-01 03:02:00.463 # no non-identical "pretty close" pair
2025-07-01 03:02:00.467 if eqi is None:
2025-07-01 03:02:00.472 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:00.477 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:00.483 return
2025-07-01 03:02:00.489 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:00.494 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:00.499 else:
2025-07-01 03:02:00.505 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:00.511 eqi = None
2025-07-01 03:02:00.516
2025-07-01 03:02:00.522 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:00.528 # identical
2025-07-01 03:02:00.534
2025-07-01 03:02:00.540 # pump out diffs from before the synch point
2025-07-01 03:02:00.545 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:00.550
2025-07-01 03:02:00.555 # do intraline marking on the synch pair
2025-07-01 03:02:00.560 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:00.564 if eqi is None:
2025-07-01 03:02:00.569 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:00.574 atags = btags = ""
2025-07-01 03:02:00.578 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:00.583 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:00.588 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:00.592 if tag == 'replace':
2025-07-01 03:02:00.596 atags += '^' * la
2025-07-01 03:02:00.601 btags += '^' * lb
2025-07-01 03:02:00.605 elif tag == 'delete':
2025-07-01 03:02:00.609 atags += '-' * la
2025-07-01 03:02:00.614 elif tag == 'insert':
2025-07-01 03:02:00.618 btags += '+' * lb
2025-07-01 03:02:00.622 elif tag == 'equal':
2025-07-01 03:02:00.627 atags += ' ' * la
2025-07-01 03:02:00.631 btags += ' ' * lb
2025-07-01 03:02:00.636 else:
2025-07-01 03:02:00.640 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:00.645 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:00.649 else:
2025-07-01 03:02:00.653 # the synch pair is identical
2025-07-01 03:02:00.657 yield '  ' + aelt
2025-07-01 03:02:00.662
2025-07-01 03:02:00.666 # pump out diffs from after the synch point
2025-07-01 03:02:00.670 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:00.675
2025-07-01 03:02:00.679 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:00.684 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:00.688
2025-07-01 03:02:00.692 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:00.697 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:00.701 alo = 190, ahi = 1101
2025-07-01 03:02:00.706 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:00.710 blo = 190, bhi = 1101
2025-07-01 03:02:00.716
2025-07-01 03:02:00.720 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:00.725 g = []
2025-07-01 03:02:00.729 if alo < ahi:
2025-07-01 03:02:00.733 if blo < bhi:
2025-07-01 03:02:00.738 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:00.742 else:
2025-07-01 03:02:00.747 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:00.751 elif blo < bhi:
2025-07-01 03:02:00.755 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:00.760
2025-07-01 03:02:00.764 >       yield from g
2025-07-01 03:02:00.769
2025-07-01 03:02:00.773 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:00.778 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:00.782
2025-07-01 03:02:00.787 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:00.791 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:00.795 alo = 190, ahi = 1101
2025-07-01 03:02:00.801 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:00.805 blo = 190, bhi = 1101
2025-07-01 03:02:00.809
2025-07-01 03:02:00.814 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:00.818 r"""
2025-07-01 03:02:00.822 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:00.827 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:00.831 synch point, and intraline difference marking is done on the
2025-07-01 03:02:00.836 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:00.840
2025-07-01 03:02:00.844 Example:
2025-07-01 03:02:00.848
2025-07-01 03:02:00.853 >>> d = Differ()
2025-07-01 03:02:00.857 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:00.862 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:00.866 >>> print(''.join(results), end="")
2025-07-01 03:02:00.870 - abcDefghiJkl
2025-07-01 03:02:00.879 + abcdefGhijkl
2025-07-01 03:02:00.887 """
2025-07-01 03:02:00.892
2025-07-01 03:02:00.896 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:00.901 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:00.905 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:00.910 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:00.915 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:00.919
2025-07-01 03:02:00.924 # search for the pair that matches best without being identical
2025-07-01 03:02:00.928 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:00.932 # on junk -- unless we have to)
2025-07-01 03:02:00.937 for j in range(blo, bhi):
2025-07-01 03:02:00.941 bj = b[j]
2025-07-01 03:02:00.946 cruncher.set_seq2(bj)
2025-07-01 03:02:00.950 for i in range(alo, ahi):
2025-07-01 03:02:00.955 ai = a[i]
2025-07-01 03:02:00.959 if ai == bj:
2025-07-01 03:02:00.964 if eqi is None:
2025-07-01 03:02:00.969 eqi, eqj = i, j
2025-07-01 03:02:00.973 continue
2025-07-01 03:02:00.978 cruncher.set_seq1(ai)
2025-07-01 03:02:00.982 # computing similarity is expensive, so use the quick
2025-07-01 03:02:00.987 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:00.991 # compares by a factor of 3.
2025-07-01 03:02:00.996 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:01.000 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:01.005 # of the computation is cached by cruncher
2025-07-01 03:02:01.010 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:01.014 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:01.019 cruncher.ratio() > best_ratio:
2025-07-01 03:02:01.023 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:01.027 if best_ratio < cutoff:
2025-07-01 03:02:01.032 # no non-identical "pretty close" pair
2025-07-01 03:02:01.037 if eqi is None:
2025-07-01 03:02:01.042 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:01.047 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:01.051 return
2025-07-01 03:02:01.056 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:01.061 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:01.065 else:
2025-07-01 03:02:01.069 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:01.074 eqi = None
2025-07-01 03:02:01.078
2025-07-01 03:02:01.083 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:01.087 # identical
2025-07-01 03:02:01.091
2025-07-01 03:02:01.096 # pump out diffs from before the synch point
2025-07-01 03:02:01.100 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:01.105
2025-07-01 03:02:01.109 # do intraline marking on the synch pair
2025-07-01 03:02:01.114 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:01.119 if eqi is None:
2025-07-01 03:02:01.123 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:01.127 atags = btags = ""
2025-07-01 03:02:01.132 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:01.136 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:01.142 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:01.148 if tag == 'replace':
2025-07-01 03:02:01.153 atags += '^' * la
2025-07-01 03:02:01.157 btags += '^' * lb
2025-07-01 03:02:01.162 elif tag == 'delete':
2025-07-01 03:02:01.167 atags += '-' * la
2025-07-01 03:02:01.171 elif tag == 'insert':
2025-07-01 03:02:01.176 btags += '+' * lb
2025-07-01 03:02:01.181 elif tag == 'equal':
2025-07-01 03:02:01.186 atags += ' ' * la
2025-07-01 03:02:01.190 btags += ' ' * lb
2025-07-01 03:02:01.195 else:
2025-07-01 03:02:01.200 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:01.204 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:01.209 else:
2025-07-01 03:02:01.213 # the synch pair is identical
2025-07-01 03:02:01.217 yield '  ' + aelt
2025-07-01 03:02:01.222
2025-07-01 03:02:01.228 # pump out diffs from after the synch point
2025-07-01 03:02:01.232 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:01.237
2025-07-01 03:02:01.243 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:01.249 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:01.255
2025-07-01 03:02:01.260 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:01.267 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:01.271 alo = 191, ahi = 1101
2025-07-01 03:02:01.277 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:01.282 blo = 191, bhi = 1101
2025-07-01 03:02:01.286
2025-07-01 03:02:01.291 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:01.295 g = []
2025-07-01 03:02:01.300 if alo < ahi:
2025-07-01 03:02:01.304 if blo < bhi:
2025-07-01 03:02:01.309 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:01.314 else:
2025-07-01 03:02:01.318 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:01.323 elif blo < bhi:
2025-07-01 03:02:01.327 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:01.332
2025-07-01 03:02:01.337 >       yield from g
2025-07-01 03:02:01.341
2025-07-01 03:02:01.346 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:01.351 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:01.355
2025-07-01 03:02:01.360 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:01.365 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:01.370 alo = 191, ahi = 1101
2025-07-01 03:02:01.375 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:01.379 blo = 191, bhi = 1101
2025-07-01 03:02:01.384
2025-07-01 03:02:01.388 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:01.393 r"""
2025-07-01 03:02:01.397 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:01.402 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:01.406 synch point, and intraline difference marking is done on the
2025-07-01 03:02:01.411 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:01.415
2025-07-01 03:02:01.419 Example:
2025-07-01 03:02:01.424
2025-07-01 03:02:01.428 >>> d = Differ()
2025-07-01 03:02:01.432 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:01.437 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:01.442 >>> print(''.join(results), end="")
2025-07-01 03:02:01.446 - abcDefghiJkl
2025-07-01 03:02:01.455 + abcdefGhijkl
2025-07-01 03:02:01.464 """
2025-07-01 03:02:01.469
2025-07-01 03:02:01.473 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:01.478 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:01.482 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:01.487 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:01.491 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:01.496
2025-07-01 03:02:01.500 # search for the pair that matches best without being identical
2025-07-01 03:02:01.504 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:01.509 # on junk -- unless we have to)
2025-07-01 03:02:01.514 for j in range(blo, bhi):
2025-07-01 03:02:01.518 bj = b[j]
2025-07-01 03:02:01.523 cruncher.set_seq2(bj)
2025-07-01 03:02:01.527 for i in range(alo, ahi):
2025-07-01 03:02:01.532 ai = a[i]
2025-07-01 03:02:01.536 if ai == bj:
2025-07-01 03:02:01.541 if eqi is None:
2025-07-01 03:02:01.547 eqi, eqj = i, j
2025-07-01 03:02:01.552 continue
2025-07-01 03:02:01.557 cruncher.set_seq1(ai)
2025-07-01 03:02:01.561 # computing similarity is expensive, so use the quick
2025-07-01 03:02:01.566 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:01.571 # compares by a factor of 3.
2025-07-01 03:02:01.575 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:01.580 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:01.584 # of the computation is cached by cruncher
2025-07-01 03:02:01.589 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:01.593 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:01.597 cruncher.ratio() > best_ratio:
2025-07-01 03:02:01.602 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:01.606 if best_ratio < cutoff:
2025-07-01 03:02:01.611 # no non-identical "pretty close" pair
2025-07-01 03:02:01.615 if eqi is None:
2025-07-01 03:02:01.619 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:01.624 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:01.628 return
2025-07-01 03:02:01.633 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:01.637 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:01.642 else:
2025-07-01 03:02:01.646 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:01.650 eqi = None
2025-07-01 03:02:01.655
2025-07-01 03:02:01.659 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:01.664 # identical
2025-07-01 03:02:01.668
2025-07-01 03:02:01.673 # pump out diffs from before the synch point
2025-07-01 03:02:01.677 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:01.682
2025-07-01 03:02:01.686 # do intraline marking on the synch pair
2025-07-01 03:02:01.692 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:01.697 if eqi is None:
2025-07-01 03:02:01.702 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:01.707 atags = btags = ""
2025-07-01 03:02:01.713 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:01.718 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:01.722 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:01.727 if tag == 'replace':
2025-07-01 03:02:01.732 atags += '^' * la
2025-07-01 03:02:01.736 btags += '^' * lb
2025-07-01 03:02:01.741 elif tag == 'delete':
2025-07-01 03:02:01.746 atags += '-' * la
2025-07-01 03:02:01.750 elif tag == 'insert':
2025-07-01 03:02:01.755 btags += '+' * lb
2025-07-01 03:02:01.760 elif tag == 'equal':
2025-07-01 03:02:01.765 atags += ' ' * la
2025-07-01 03:02:01.769 btags += ' ' * lb
2025-07-01 03:02:01.774 else:
2025-07-01 03:02:01.779 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:01.784 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:01.788 else:
2025-07-01 03:02:01.793 # the synch pair is identical
2025-07-01 03:02:01.797 yield '  ' + aelt
2025-07-01 03:02:01.802
2025-07-01 03:02:01.807 # pump out diffs from after the synch point
2025-07-01 03:02:01.811 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:01.815
2025-07-01 03:02:01.820 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:01.824 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:01.829
2025-07-01 03:02:01.834 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:01.838 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:01.843 alo = 192, ahi = 1101
2025-07-01 03:02:01.848 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:01.854 blo = 192, bhi = 1101
2025-07-01 03:02:01.858
2025-07-01 03:02:01.863 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:01.868 g = []
2025-07-01 03:02:01.872 if alo < ahi:
2025-07-01 03:02:01.877 if blo < bhi:
2025-07-01 03:02:01.881 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:01.886 else:
2025-07-01 03:02:01.891 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:01.895 elif blo < bhi:
2025-07-01 03:02:01.900 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:01.904
2025-07-01 03:02:01.908 >       yield from g
2025-07-01 03:02:01.913
2025-07-01 03:02:01.917 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:01.922 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:01.927
2025-07-01 03:02:01.931 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:01.936 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:01.941 alo = 192, ahi = 1101
2025-07-01 03:02:01.946 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:01.950 blo = 192, bhi = 1101
2025-07-01 03:02:01.955
2025-07-01 03:02:01.959 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:01.964 r"""
2025-07-01 03:02:01.969 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:01.975 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:01.981 synch point, and intraline difference marking is done on the
2025-07-01 03:02:01.987 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:01.994
2025-07-01 03:02:02.001 Example:
2025-07-01 03:02:02.007
2025-07-01 03:02:02.013 >>> d = Differ()
2025-07-01 03:02:02.019 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:02.025 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:02.030 >>> print(''.join(results), end="")
2025-07-01 03:02:02.037 - abcDefghiJkl
2025-07-01 03:02:02.047 + abcdefGhijkl
2025-07-01 03:02:02.056 """
2025-07-01 03:02:02.061
2025-07-01 03:02:02.066 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:02.071 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:02.075 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:02.080 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:02.085 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:02.090
2025-07-01 03:02:02.095 # search for the pair that matches best without being identical
2025-07-01 03:02:02.101 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:02.105 # on junk -- unless we have to)
2025-07-01 03:02:02.110 for j in range(blo, bhi):
2025-07-01 03:02:02.114 bj = b[j]
2025-07-01 03:02:02.119 cruncher.set_seq2(bj)
2025-07-01 03:02:02.124 for i in range(alo, ahi):
2025-07-01 03:02:02.128 ai = a[i]
2025-07-01 03:02:02.132 if ai == bj:
2025-07-01 03:02:02.137 if eqi is None:
2025-07-01 03:02:02.142 eqi, eqj = i, j
2025-07-01 03:02:02.146 continue
2025-07-01 03:02:02.151 cruncher.set_seq1(ai)
2025-07-01 03:02:02.155 # computing similarity is expensive, so use the quick
2025-07-01 03:02:02.160 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:02.164 # compares by a factor of 3.
2025-07-01 03:02:02.169 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:02.174 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:02.178 # of the computation is cached by cruncher
2025-07-01 03:02:02.183 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:02.187 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:02.192 cruncher.ratio() > best_ratio:
2025-07-01 03:02:02.198 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:02.203 if best_ratio < cutoff:
2025-07-01 03:02:02.208 # no non-identical "pretty close" pair
2025-07-01 03:02:02.212 if eqi is None:
2025-07-01 03:02:02.217 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:02.221 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:02.225 return
2025-07-01 03:02:02.230 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:02.234 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:02.238 else:
2025-07-01 03:02:02.243 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:02.247 eqi = None
2025-07-01 03:02:02.251
2025-07-01 03:02:02.256 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:02.261 # identical
2025-07-01 03:02:02.265
2025-07-01 03:02:02.270 # pump out diffs from before the synch point
2025-07-01 03:02:02.275 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:02.279
2025-07-01 03:02:02.285 # do intraline marking on the synch pair
2025-07-01 03:02:02.289 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:02.293 if eqi is None:
2025-07-01 03:02:02.298 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:02.302 atags = btags = ""
2025-07-01 03:02:02.306 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:02.311 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:02.315 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:02.319 if tag == 'replace':
2025-07-01 03:02:02.324 atags += '^' * la
2025-07-01 03:02:02.328 btags += '^' * lb
2025-07-01 03:02:02.332 elif tag == 'delete':
2025-07-01 03:02:02.337 atags += '-' * la
2025-07-01 03:02:02.341 elif tag == 'insert':
2025-07-01 03:02:02.345 btags += '+' * lb
2025-07-01 03:02:02.349 elif tag == 'equal':
2025-07-01 03:02:02.354 atags += ' ' * la
2025-07-01 03:02:02.359 btags += ' ' * lb
2025-07-01 03:02:02.363 else:
2025-07-01 03:02:02.368 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:02.373 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:02.380 else:
2025-07-01 03:02:02.386 # the synch pair is identical
2025-07-01 03:02:02.392 yield '  ' + aelt
2025-07-01 03:02:02.398
2025-07-01 03:02:02.404 # pump out diffs from after the synch point
2025-07-01 03:02:02.410 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:02.417
2025-07-01 03:02:02.422 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:02.429 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:02.434
2025-07-01 03:02:02.440 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:02.446 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:02.451 alo = 193, ahi = 1101
2025-07-01 03:02:02.458 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:02.463 blo = 193, bhi = 1101
2025-07-01 03:02:02.469
2025-07-01 03:02:02.475 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:02.481 g = []
2025-07-01 03:02:02.487 if alo < ahi:
2025-07-01 03:02:02.504 if blo < bhi:
2025-07-01 03:02:02.511 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:02.518 else:
2025-07-01 03:02:02.524 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:02.530 elif blo < bhi:
2025-07-01 03:02:02.536 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:02.542
2025-07-01 03:02:02.549 >       yield from g
2025-07-01 03:02:02.555
2025-07-01 03:02:02.561 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:02.568 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:02.574
2025-07-01 03:02:02.581 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:02.587 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:02.594 alo = 193, ahi = 1101
2025-07-01 03:02:02.601 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:02.607 blo = 193, bhi = 1101
2025-07-01 03:02:02.614
2025-07-01 03:02:02.621 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:02.631 r"""
2025-07-01 03:02:02.650 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:02.669 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:02.686 synch point, and intraline difference marking is done on the
2025-07-01 03:02:02.701 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:02.715
2025-07-01 03:02:02.727 Example:
2025-07-01 03:02:02.739
2025-07-01 03:02:02.754 >>> d = Differ()
2025-07-01 03:02:02.776 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:02.790 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:02.801 >>> print(''.join(results), end="")
2025-07-01 03:02:02.814 - abcDefghiJkl
2025-07-01 03:02:02.851 + abcdefGhijkl
2025-07-01 03:02:02.882 """
2025-07-01 03:02:02.894
2025-07-01 03:02:02.910 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:02.923 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:02.934 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:02.946 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:02.958 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:02.974
2025-07-01 03:02:02.990 # search for the pair that matches best without being identical
2025-07-01 03:02:03.009 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:03.027 # on junk -- unless we have to)
2025-07-01 03:02:03.042 for j in range(blo, bhi):
2025-07-01 03:02:03.054 bj = b[j]
2025-07-01 03:02:03.070 cruncher.set_seq2(bj)
2025-07-01 03:02:03.086 for i in range(alo, ahi):
2025-07-01 03:02:03.094 ai = a[i]
2025-07-01 03:02:03.110 if ai == bj:
2025-07-01 03:02:03.126 if eqi is None:
2025-07-01 03:02:03.138 eqi, eqj = i, j
2025-07-01 03:02:03.150 continue
2025-07-01 03:02:03.158 cruncher.set_seq1(ai)
2025-07-01 03:02:03.178 # computing similarity is expensive, so use the quick
2025-07-01 03:02:03.194 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:03.211 # compares by a factor of 3.
2025-07-01 03:02:03.223 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:03.244 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:03.258 # of the computation is cached by cruncher
2025-07-01 03:02:03.274 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:03.288 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:03.299 cruncher.ratio() > best_ratio:
2025-07-01 03:02:03.314 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:03.322 if best_ratio < cutoff:
2025-07-01 03:02:03.338 # no non-identical "pretty close" pair
2025-07-01 03:02:03.354 if eqi is None:
2025-07-01 03:02:03.370 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:03.383 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:03.402 return
2025-07-01 03:02:03.415 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:03.425 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:03.431 else:
2025-07-01 03:02:03.437 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:03.443 eqi = None
2025-07-01 03:02:03.449
2025-07-01 03:02:03.455 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:03.461 # identical
2025-07-01 03:02:03.467
2025-07-01 03:02:03.473 # pump out diffs from before the synch point
2025-07-01 03:02:03.479 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:03.485
2025-07-01 03:02:03.491 # do intraline marking on the synch pair
2025-07-01 03:02:03.497 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:03.504 if eqi is None:
2025-07-01 03:02:03.510 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:03.515 atags = btags = ""
2025-07-01 03:02:03.522 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:03.546 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:03.566 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:03.582 if tag == 'replace':
2025-07-01 03:02:03.590 atags += '^' * la
2025-07-01 03:02:03.601 btags += '^' * lb
2025-07-01 03:02:03.614 elif tag == 'delete':
2025-07-01 03:02:03.626 atags += '-' * la
2025-07-01 03:02:03.634 elif tag == 'insert':
2025-07-01 03:02:03.650 btags += '+' * lb
2025-07-01 03:02:03.661 elif tag == 'equal':
2025-07-01 03:02:03.678 atags += ' ' * la
2025-07-01 03:02:03.690 btags += ' ' * lb
2025-07-01 03:02:03.702 else:
2025-07-01 03:02:03.720 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:03.731 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:03.753 else:
2025-07-01 03:02:03.761 # the synch pair is identical
2025-07-01 03:02:03.775 yield '  ' + aelt
2025-07-01 03:02:03.794
2025-07-01 03:02:03.810 # pump out diffs from after the synch point
2025-07-01 03:02:03.822 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:03.838
2025-07-01 03:02:03.854 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:03.870 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:03.885
2025-07-01 03:02:03.898 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:03.918 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:03.927 alo = 194, ahi = 1101
2025-07-01 03:02:03.946 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:03.958 blo = 194, bhi = 1101
2025-07-01 03:02:03.971
2025-07-01 03:02:03.980 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:03.986 g = []
2025-07-01 03:02:03.995 if alo < ahi:
2025-07-01 03:02:04.018 if blo < bhi:
2025-07-01 03:02:04.030 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:04.042 else:
2025-07-01 03:02:04.049 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:04.062 elif blo < bhi:
2025-07-01 03:02:04.082 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:04.102
2025-07-01 03:02:04.118 >       yield from g
2025-07-01 03:02:04.134
2025-07-01 03:02:04.149 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:04.161 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:04.178
2025-07-01 03:02:04.186 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:04.202 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:04.218 alo = 194, ahi = 1101
2025-07-01 03:02:04.238 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:04.255 blo = 194, bhi = 1101
2025-07-01 03:02:04.268
2025-07-01 03:02:04.283 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:04.301 r"""
2025-07-01 03:02:04.318 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:04.325 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:04.343 synch point, and intraline difference marking is done on the
2025-07-01 03:02:04.358 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:04.378
2025-07-01 03:02:04.394 Example:
2025-07-01 03:02:04.406
2025-07-01 03:02:04.429 >>> d = Differ()
2025-07-01 03:02:04.438 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:04.457 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:04.470 >>> print(''.join(results), end="")
2025-07-01 03:02:04.481 - abcDefghiJkl
2025-07-01 03:02:04.510 + abcdefGhijkl
2025-07-01 03:02:04.542 """
2025-07-01 03:02:04.558
2025-07-01 03:02:04.570 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:04.586 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:04.602 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:04.610 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:04.622 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:04.643
2025-07-01 03:02:04.651 # search for the pair that matches best without being identical
2025-07-01 03:02:04.662 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:04.677 # on junk -- unless we have to)
2025-07-01 03:02:04.689 for j in range(blo, bhi):
2025-07-01 03:02:04.695 bj = b[j]
2025-07-01 03:02:04.701 cruncher.set_seq2(bj)
2025-07-01 03:02:04.707 for i in range(alo, ahi):
2025-07-01 03:02:04.713 ai = a[i]
2025-07-01 03:02:04.718 if ai == bj:
2025-07-01 03:02:04.724 if eqi is None:
2025-07-01 03:02:04.733 eqi, eqj = i, j
2025-07-01 03:02:04.750 continue
2025-07-01 03:02:04.762 cruncher.set_seq1(ai)
2025-07-01 03:02:04.770 # computing similarity is expensive, so use the quick
2025-07-01 03:02:04.790 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:04.806 # compares by a factor of 3.
2025-07-01 03:02:04.822 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:04.838 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:04.849 # of the computation is cached by cruncher
2025-07-01 03:02:04.862 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:04.874 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:04.886 cruncher.ratio() > best_ratio:
2025-07-01 03:02:04.897 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:04.910 if best_ratio < cutoff:
2025-07-01 03:02:04.923 # no non-identical "pretty close" pair
2025-07-01 03:02:04.942 if eqi is None:
2025-07-01 03:02:04.951 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:04.973 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:04.989 return
2025-07-01 03:02:05.005 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:05.015 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:05.028 else:
2025-07-01 03:02:05.047 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:05.062 eqi = None
2025-07-01 03:02:05.070
2025-07-01 03:02:05.082 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:05.094 # identical
2025-07-01 03:02:05.110
2025-07-01 03:02:05.122 # pump out diffs from before the synch point
2025-07-01 03:02:05.138 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:05.150
2025-07-01 03:02:05.161 # do intraline marking on the synch pair
2025-07-01 03:02:05.176 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:05.185 if eqi is None:
2025-07-01 03:02:05.193 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:05.207 atags = btags = ""
2025-07-01 03:02:05.218 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:05.225 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:05.231 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:05.237 if tag == 'replace':
2025-07-01 03:02:05.243 atags += '^' * la
2025-07-01 03:02:05.248 btags += '^' * lb
2025-07-01 03:02:05.254 elif tag == 'delete':
2025-07-01 03:02:05.260 atags += '-' * la
2025-07-01 03:02:05.266 elif tag == 'insert':
2025-07-01 03:02:05.272 btags += '+' * lb
2025-07-01 03:02:05.278 elif tag == 'equal':
2025-07-01 03:02:05.284 atags += ' ' * la
2025-07-01 03:02:05.290 btags += ' ' * lb
2025-07-01 03:02:05.296 else:
2025-07-01 03:02:05.303 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:05.309 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:05.315 else:
2025-07-01 03:02:05.322 # the synch pair is identical
2025-07-01 03:02:05.328 yield '  ' + aelt
2025-07-01 03:02:05.334
2025-07-01 03:02:05.341 # pump out diffs from after the synch point
2025-07-01 03:02:05.347 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:05.353
2025-07-01 03:02:05.359 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:05.366 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:05.372
2025-07-01 03:02:05.378 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:05.385 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:05.391 alo = 195, ahi = 1101
2025-07-01 03:02:05.398 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:05.404 blo = 195, bhi = 1101
2025-07-01 03:02:05.410
2025-07-01 03:02:05.416 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:05.421 g = []
2025-07-01 03:02:05.427 if alo < ahi:
2025-07-01 03:02:05.433 if blo < bhi:
2025-07-01 03:02:05.439 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:05.445 else:
2025-07-01 03:02:05.451 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:05.457 elif blo < bhi:
2025-07-01 03:02:05.463 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:05.469
2025-07-01 03:02:05.474 >       yield from g
2025-07-01 03:02:05.480
2025-07-01 03:02:05.486 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:05.492 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:05.498
2025-07-01 03:02:05.505 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:05.512 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:05.518 alo = 195, ahi = 1101
2025-07-01 03:02:05.525 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:05.531 blo = 195, bhi = 1101
2025-07-01 03:02:05.537
2025-07-01 03:02:05.543 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:05.549 r"""
2025-07-01 03:02:05.555 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:05.562 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:05.568 synch point, and intraline difference marking is done on the
2025-07-01 03:02:05.574 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:05.580
2025-07-01 03:02:05.586 Example:
2025-07-01 03:02:05.592
2025-07-01 03:02:05.599 >>> d = Differ()
2025-07-01 03:02:05.605 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:05.611 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:05.617 >>> print(''.join(results), end="")
2025-07-01 03:02:05.623 - abcDefghiJkl
2025-07-01 03:02:05.635 + abcdefGhijkl
2025-07-01 03:02:05.648 """
2025-07-01 03:02:05.654
2025-07-01 03:02:05.660 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:05.667 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:05.673 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:05.679 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:05.686 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:05.692
2025-07-01 03:02:05.699 # search for the pair that matches best without being identical
2025-07-01 03:02:05.706 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:05.712 # on junk -- unless we have to)
2025-07-01 03:02:05.718 for j in range(blo, bhi):
2025-07-01 03:02:05.725 bj = b[j]
2025-07-01 03:02:05.731 cruncher.set_seq2(bj)
2025-07-01 03:02:05.737 for i in range(alo, ahi):
2025-07-01 03:02:05.743 ai = a[i]
2025-07-01 03:02:05.750 if ai == bj:
2025-07-01 03:02:05.756 if eqi is None:
2025-07-01 03:02:05.763 eqi, eqj = i, j
2025-07-01 03:02:05.769 continue
2025-07-01 03:02:05.776 cruncher.set_seq1(ai)
2025-07-01 03:02:05.782 # computing similarity is expensive, so use the quick
2025-07-01 03:02:05.789 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:05.795 # compares by a factor of 3.
2025-07-01 03:02:05.801 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:05.808 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:05.814 # of the computation is cached by cruncher
2025-07-01 03:02:05.820 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:05.826 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:05.833 cruncher.ratio() > best_ratio:
2025-07-01 03:02:05.839 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:05.845 if best_ratio < cutoff:
2025-07-01 03:02:05.851 # no non-identical "pretty close" pair
2025-07-01 03:02:05.857 if eqi is None:
2025-07-01 03:02:05.864 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:05.870 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:05.876 return
2025-07-01 03:02:05.882 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:05.888 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:05.894 else:
2025-07-01 03:02:05.901 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:05.907 eqi = None
2025-07-01 03:02:05.912
2025-07-01 03:02:05.918 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:05.924 # identical
2025-07-01 03:02:05.930
2025-07-01 03:02:05.936 # pump out diffs from before the synch point
2025-07-01 03:02:05.942 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:05.948
2025-07-01 03:02:05.954 # do intraline marking on the synch pair
2025-07-01 03:02:05.961 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:05.967 if eqi is None:
2025-07-01 03:02:05.973 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:05.979 atags = btags = ""
2025-07-01 03:02:05.985 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:05.991 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:05.998 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:06.004 if tag == 'replace':
2025-07-01 03:02:06.010 atags += '^' * la
2025-07-01 03:02:06.017 btags += '^' * lb
2025-07-01 03:02:06.023 elif tag == 'delete':
2025-07-01 03:02:06.029 atags += '-' * la
2025-07-01 03:02:06.035 elif tag == 'insert':
2025-07-01 03:02:06.041 btags += '+' * lb
2025-07-01 03:02:06.047 elif tag == 'equal':
2025-07-01 03:02:06.053 atags += ' ' * la
2025-07-01 03:02:06.059 btags += ' ' * lb
2025-07-01 03:02:06.065 else:
2025-07-01 03:02:06.072 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:06.079 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:06.086 else:
2025-07-01 03:02:06.093 # the synch pair is identical
2025-07-01 03:02:06.100 yield '  ' + aelt
2025-07-01 03:02:06.107
2025-07-01 03:02:06.113 # pump out diffs from after the synch point
2025-07-01 03:02:06.120 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:06.126
2025-07-01 03:02:06.132 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:06.139 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:06.145
2025-07-01 03:02:06.151 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:06.158 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:06.164 alo = 196, ahi = 1101
2025-07-01 03:02:06.171 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:06.177 blo = 196, bhi = 1101
2025-07-01 03:02:06.184
2025-07-01 03:02:06.190 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:06.196 g = []
2025-07-01 03:02:06.202 if alo < ahi:
2025-07-01 03:02:06.208 if blo < bhi:
2025-07-01 03:02:06.214 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:06.220 else:
2025-07-01 03:02:06.226 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:06.232 elif blo < bhi:
2025-07-01 03:02:06.238 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:06.243
2025-07-01 03:02:06.249 >       yield from g
2025-07-01 03:02:06.255
2025-07-01 03:02:06.263 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:06.269 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:06.275
2025-07-01 03:02:06.282 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:06.288 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:06.294 alo = 196, ahi = 1101
2025-07-01 03:02:06.301 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:06.307 blo = 196, bhi = 1101
2025-07-01 03:02:06.313
2025-07-01 03:02:06.319 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:06.325 r"""
2025-07-01 03:02:06.331 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:06.337 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:06.343 synch point, and intraline difference marking is done on the
2025-07-01 03:02:06.349 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:06.355
2025-07-01 03:02:06.360 Example:
2025-07-01 03:02:06.366
2025-07-01 03:02:06.372 >>> d = Differ()
2025-07-01 03:02:06.378 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:06.384 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:06.390 >>> print(''.join(results), end="")
2025-07-01 03:02:06.396 - abcDefghiJkl
2025-07-01 03:02:06.408 + abcdefGhijkl
2025-07-01 03:02:06.419 """
2025-07-01 03:02:06.425
2025-07-01 03:02:06.431 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:06.438 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:06.445 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:06.451 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:06.458 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:06.465
2025-07-01 03:02:06.479 # search for the pair that matches best without being identical
2025-07-01 03:02:06.492 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:06.497 # on junk -- unless we have to)
2025-07-01 03:02:06.503 for j in range(blo, bhi):
2025-07-01 03:02:06.509 bj = b[j]
2025-07-01 03:02:06.514 cruncher.set_seq2(bj)
2025-07-01 03:02:06.520 for i in range(alo, ahi):
2025-07-01 03:02:06.525 ai = a[i]
2025-07-01 03:02:06.531 if ai == bj:
2025-07-01 03:02:06.537 if eqi is None:
2025-07-01 03:02:06.542 eqi, eqj = i, j
2025-07-01 03:02:06.548 continue
2025-07-01 03:02:06.553 cruncher.set_seq1(ai)
2025-07-01 03:02:06.559 # computing similarity is expensive, so use the quick
2025-07-01 03:02:06.565 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:06.570 # compares by a factor of 3.
2025-07-01 03:02:06.576 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:06.582 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:06.589 # of the computation is cached by cruncher
2025-07-01 03:02:06.595 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:06.600 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:06.606 cruncher.ratio() > best_ratio:
2025-07-01 03:02:06.612 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:06.618 if best_ratio < cutoff:
2025-07-01 03:02:06.623 # no non-identical "pretty close" pair
2025-07-01 03:02:06.629 if eqi is None:
2025-07-01 03:02:06.635 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:06.640 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:06.646 return
2025-07-01 03:02:06.652 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:06.657 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:06.663 else:
2025-07-01 03:02:06.669 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:06.674 eqi = None
2025-07-01 03:02:06.679
2025-07-01 03:02:06.685 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:06.691 # identical
2025-07-01 03:02:06.696
2025-07-01 03:02:06.702 # pump out diffs from before the synch point
2025-07-01 03:02:06.707 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:06.713
2025-07-01 03:02:06.718 # do intraline marking on the synch pair
2025-07-01 03:02:06.724 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:06.730 if eqi is None:
2025-07-01 03:02:06.736 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:06.741 atags = btags = ""
2025-07-01 03:02:06.747 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:06.752 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:06.758 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:06.763 if tag == 'replace':
2025-07-01 03:02:06.769 atags += '^' * la
2025-07-01 03:02:06.774 btags += '^' * lb
2025-07-01 03:02:06.779 elif tag == 'delete':
2025-07-01 03:02:06.785 atags += '-' * la
2025-07-01 03:02:06.790 elif tag == 'insert':
2025-07-01 03:02:06.796 btags += '+' * lb
2025-07-01 03:02:06.801 elif tag == 'equal':
2025-07-01 03:02:06.807 atags += ' ' * la
2025-07-01 03:02:06.812 btags += ' ' * lb
2025-07-01 03:02:06.817 else:
2025-07-01 03:02:06.823 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:06.828 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:06.834 else:
2025-07-01 03:02:06.839 # the synch pair is identical
2025-07-01 03:02:06.845 yield '  ' + aelt
2025-07-01 03:02:06.851
2025-07-01 03:02:06.857 # pump out diffs from after the synch point
2025-07-01 03:02:06.863 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:06.869
2025-07-01 03:02:06.875 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:06.881 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:06.887
2025-07-01 03:02:06.893 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:06.899 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:06.905 alo = 197, ahi = 1101
2025-07-01 03:02:06.912 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:06.918 blo = 197, bhi = 1101
2025-07-01 03:02:06.924
2025-07-01 03:02:06.930 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:06.936 g = []
2025-07-01 03:02:06.947 if alo < ahi:
2025-07-01 03:02:06.956 if blo < bhi:
2025-07-01 03:02:06.962 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:06.968 else:
2025-07-01 03:02:06.974 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:06.980 elif blo < bhi:
2025-07-01 03:02:06.986 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:06.992
2025-07-01 03:02:06.998 >       yield from g
2025-07-01 03:02:07.003
2025-07-01 03:02:07.009 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:07.015 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:07.020
2025-07-01 03:02:07.026 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:07.032 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:07.037 alo = 197, ahi = 1101
2025-07-01 03:02:07.044 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:07.049 blo = 197, bhi = 1101
2025-07-01 03:02:07.054
2025-07-01 03:02:07.060 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:07.066 r"""
2025-07-01 03:02:07.072 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:07.077 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:07.083 synch point, and intraline difference marking is done on the
2025-07-01 03:02:07.088 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:07.094
2025-07-01 03:02:07.099 Example:
2025-07-01 03:02:07.105
2025-07-01 03:02:07.111 >>> d = Differ()
2025-07-01 03:02:07.116 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:07.122 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:07.127 >>> print(''.join(results), end="")
2025-07-01 03:02:07.133 - abcDefghiJkl
2025-07-01 03:02:07.144 + abcdefGhijkl
2025-07-01 03:02:07.156 """
2025-07-01 03:02:07.161
2025-07-01 03:02:07.167 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:07.173 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:07.178 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:07.184 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:07.190 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:07.196
2025-07-01 03:02:07.201 # search for the pair that matches best without being identical
2025-07-01 03:02:07.207 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:07.213 # on junk -- unless we have to)
2025-07-01 03:02:07.219 for j in range(blo, bhi):
2025-07-01 03:02:07.225 bj = b[j]
2025-07-01 03:02:07.230 cruncher.set_seq2(bj)
2025-07-01 03:02:07.236 for i in range(alo, ahi):
2025-07-01 03:02:07.242 ai = a[i]
2025-07-01 03:02:07.248 if ai == bj:
2025-07-01 03:02:07.254 if eqi is None:
2025-07-01 03:02:07.259 eqi, eqj = i, j
2025-07-01 03:02:07.265 continue
2025-07-01 03:02:07.270 cruncher.set_seq1(ai)
2025-07-01 03:02:07.276 # computing similarity is expensive, so use the quick
2025-07-01 03:02:07.282 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:07.288 # compares by a factor of 3.
2025-07-01 03:02:07.293 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:07.299 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:07.305 # of the computation is cached by cruncher
2025-07-01 03:02:07.311 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:07.317 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:07.322 cruncher.ratio() > best_ratio:
2025-07-01 03:02:07.328 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:07.334 if best_ratio < cutoff:
2025-07-01 03:02:07.339 # no non-identical "pretty close" pair
2025-07-01 03:02:07.345 if eqi is None:
2025-07-01 03:02:07.351 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:07.357 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:07.362 return
2025-07-01 03:02:07.368 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:07.374 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:07.380 else:
2025-07-01 03:02:07.386 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:07.391 eqi = None
2025-07-01 03:02:07.397
2025-07-01 03:02:07.403 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:07.408 # identical
2025-07-01 03:02:07.414
2025-07-01 03:02:07.420 # pump out diffs from before the synch point
2025-07-01 03:02:07.426 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:07.431
2025-07-01 03:02:07.437 # do intraline marking on the synch pair
2025-07-01 03:02:07.443 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:07.448 if eqi is None:
2025-07-01 03:02:07.454 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:07.459 atags = btags = ""
2025-07-01 03:02:07.465 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:07.471 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:07.477 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:07.482 if tag == 'replace':
2025-07-01 03:02:07.488 atags += '^' * la
2025-07-01 03:02:07.493 btags += '^' * lb
2025-07-01 03:02:07.499 elif tag == 'delete':
2025-07-01 03:02:07.505 atags += '-' * la
2025-07-01 03:02:07.510 elif tag == 'insert':
2025-07-01 03:02:07.516 btags += '+' * lb
2025-07-01 03:02:07.522 elif tag == 'equal':
2025-07-01 03:02:07.527 atags += ' ' * la
2025-07-01 03:02:07.533 btags += ' ' * lb
2025-07-01 03:02:07.539 else:
2025-07-01 03:02:07.544 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:07.550 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:07.556 else:
2025-07-01 03:02:07.562 # the synch pair is identical
2025-07-01 03:02:07.567 yield '  ' + aelt
2025-07-01 03:02:07.573
2025-07-01 03:02:07.578 # pump out diffs from after the synch point
2025-07-01 03:02:07.584 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:07.590
2025-07-01 03:02:07.595 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:07.601 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:07.607
2025-07-01 03:02:07.614 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:07.620 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:07.626 alo = 198, ahi = 1101
2025-07-01 03:02:07.632 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:07.638 blo = 198, bhi = 1101
2025-07-01 03:02:07.644
2025-07-01 03:02:07.649 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:07.655 g = []
2025-07-01 03:02:07.661 if alo < ahi:
2025-07-01 03:02:07.666 if blo < bhi:
2025-07-01 03:02:07.672 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:07.677 else:
2025-07-01 03:02:07.683 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:07.689 elif blo < bhi:
2025-07-01 03:02:07.695 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:07.700
2025-07-01 03:02:07.706 >       yield from g
2025-07-01 03:02:07.711
2025-07-01 03:02:07.717 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:07.723 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:07.728
2025-07-01 03:02:07.734 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:07.740 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:07.746 alo = 198, ahi = 1101
2025-07-01 03:02:07.752 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:07.758 blo = 198, bhi = 1101
2025-07-01 03:02:07.763
2025-07-01 03:02:07.769 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:07.775 r"""
2025-07-01 03:02:07.780 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:07.786 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:07.792 synch point, and intraline difference marking is done on the
2025-07-01 03:02:07.797 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:07.803
2025-07-01 03:02:07.808 Example:
2025-07-01 03:02:07.814
2025-07-01 03:02:07.819 >>> d = Differ()
2025-07-01 03:02:07.825 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:07.831 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:07.837 >>> print(''.join(results), end="")
2025-07-01 03:02:07.843 - abcDefghiJkl
2025-07-01 03:02:07.854 + abcdefGhijkl
2025-07-01 03:02:07.865 """
2025-07-01 03:02:07.871
2025-07-01 03:02:07.877 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:07.882 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:07.888 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:07.894 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:07.899 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:07.905
2025-07-01 03:02:07.911 # search for the pair that matches best without being identical
2025-07-01 03:02:07.917 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:07.922 # on junk -- unless we have to)
2025-07-01 03:02:07.934 for j in range(blo, bhi):
2025-07-01 03:02:07.944 bj = b[j]
2025-07-01 03:02:07.952 cruncher.set_seq2(bj)
2025-07-01 03:02:07.957 for i in range(alo, ahi):
2025-07-01 03:02:07.962 ai = a[i]
2025-07-01 03:02:07.967 if ai == bj:
2025-07-01 03:02:07.973 if eqi is None:
2025-07-01 03:02:07.977 eqi, eqj = i, j
2025-07-01 03:02:07.981 continue
2025-07-01 03:02:07.986 cruncher.set_seq1(ai)
2025-07-01 03:02:07.990 # computing similarity is expensive, so use the quick
2025-07-01 03:02:07.996 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:08.000 # compares by a factor of 3.
2025-07-01 03:02:08.005 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:08.010 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:08.015 # of the computation is cached by cruncher
2025-07-01 03:02:08.019 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:08.024 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:08.028 cruncher.ratio() > best_ratio:
2025-07-01 03:02:08.033 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:08.038 if best_ratio < cutoff:
2025-07-01 03:02:08.043 # no non-identical "pretty close" pair
2025-07-01 03:02:08.047 if eqi is None:
2025-07-01 03:02:08.053 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:08.057 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:08.062 return
2025-07-01 03:02:08.067 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:08.071 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:08.076 else:
2025-07-01 03:02:08.081 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:08.085 eqi = None
2025-07-01 03:02:08.090
2025-07-01 03:02:08.095 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:08.100 # identical
2025-07-01 03:02:08.105
2025-07-01 03:02:08.110 # pump out diffs from before the synch point
2025-07-01 03:02:08.115 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:08.119
2025-07-01 03:02:08.124 # do intraline marking on the synch pair
2025-07-01 03:02:08.129 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:08.135 if eqi is None:
2025-07-01 03:02:08.139 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:08.143 atags = btags = ""
2025-07-01 03:02:08.148 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:08.152 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:08.157 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:08.162 if tag == 'replace':
2025-07-01 03:02:08.167 atags += '^' * la
2025-07-01 03:02:08.172 btags += '^' * lb
2025-07-01 03:02:08.176 elif tag == 'delete':
2025-07-01 03:02:08.181 atags += '-' * la
2025-07-01 03:02:08.185 elif tag == 'insert':
2025-07-01 03:02:08.191 btags += '+' * lb
2025-07-01 03:02:08.195 elif tag == 'equal':
2025-07-01 03:02:08.200 atags += ' ' * la
2025-07-01 03:02:08.205 btags += ' ' * lb
2025-07-01 03:02:08.209 else:
2025-07-01 03:02:08.214 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:08.218 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:08.222 else:
2025-07-01 03:02:08.227 # the synch pair is identical
2025-07-01 03:02:08.231 yield '  ' + aelt
2025-07-01 03:02:08.236
2025-07-01 03:02:08.240 # pump out diffs from after the synch point
2025-07-01 03:02:08.245 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:08.249
2025-07-01 03:02:08.253 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:08.258 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:08.262
2025-07-01 03:02:08.267 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:08.271 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:08.276 alo = 199, ahi = 1101
2025-07-01 03:02:08.282 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:08.286 blo = 199, bhi = 1101
2025-07-01 03:02:08.290
2025-07-01 03:02:08.294 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:08.299 g = []
2025-07-01 03:02:08.304 if alo < ahi:
2025-07-01 03:02:08.308 if blo < bhi:
2025-07-01 03:02:08.313 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:08.317 else:
2025-07-01 03:02:08.322 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:08.328 elif blo < bhi:
2025-07-01 03:02:08.333 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:08.337
2025-07-01 03:02:08.341 >       yield from g
2025-07-01 03:02:08.346
2025-07-01 03:02:08.350 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:08.354 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:08.359
2025-07-01 03:02:08.363 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:08.369 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:08.373 alo = 199, ahi = 1101
2025-07-01 03:02:08.378 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:08.383 blo = 199, bhi = 1101
2025-07-01 03:02:08.387
2025-07-01 03:02:08.392 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:08.396 r"""
2025-07-01 03:02:08.400 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:08.405 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:08.409 synch point, and intraline difference marking is done on the
2025-07-01 03:02:08.414 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:08.418
2025-07-01 03:02:08.422 Example:
2025-07-01 03:02:08.427
2025-07-01 03:02:08.432 >>> d = Differ()
2025-07-01 03:02:08.437 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:08.442 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:08.447 >>> print(''.join(results), end="")
2025-07-01 03:02:08.451 - abcDefghiJkl
2025-07-01 03:02:08.460 + abcdefGhijkl
2025-07-01 03:02:08.469 """
2025-07-01 03:02:08.473
2025-07-01 03:02:08.478 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:08.483 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:08.488 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:08.492 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:08.498 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:08.502
2025-07-01 03:02:08.507 # search for the pair that matches best without being identical
2025-07-01 03:02:08.511 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:08.516 # on junk -- unless we have to)
2025-07-01 03:02:08.520 for j in range(blo, bhi):
2025-07-01 03:02:08.525 bj = b[j]
2025-07-01 03:02:08.529 cruncher.set_seq2(bj)
2025-07-01 03:02:08.534 for i in range(alo, ahi):
2025-07-01 03:02:08.538 ai = a[i]
2025-07-01 03:02:08.543 if ai == bj:
2025-07-01 03:02:08.548 if eqi is None:
2025-07-01 03:02:08.552 eqi, eqj = i, j
2025-07-01 03:02:08.558 continue
2025-07-01 03:02:08.563 cruncher.set_seq1(ai)
2025-07-01 03:02:08.568 # computing similarity is expensive, so use the quick
2025-07-01 03:02:08.573 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:08.578 # compares by a factor of 3.
2025-07-01 03:02:08.583 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:08.588 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:08.593 # of the computation is cached by cruncher
2025-07-01 03:02:08.599 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:08.604 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:08.608 cruncher.ratio() > best_ratio:
2025-07-01 03:02:08.613 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:08.618 if best_ratio < cutoff:
2025-07-01 03:02:08.623 # no non-identical "pretty close" pair
2025-07-01 03:02:08.627 if eqi is None:
2025-07-01 03:02:08.632 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:08.636 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:08.641 return
2025-07-01 03:02:08.645 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:08.650 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:08.655 else:
2025-07-01 03:02:08.659 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:08.663 eqi = None
2025-07-01 03:02:08.668
2025-07-01 03:02:08.672 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:08.677 # identical
2025-07-01 03:02:08.681
2025-07-01 03:02:08.686 # pump out diffs from before the synch point
2025-07-01 03:02:08.691 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:08.695
2025-07-01 03:02:08.699 # do intraline marking on the synch pair
2025-07-01 03:02:08.704 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:08.708 if eqi is None:
2025-07-01 03:02:08.713 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:08.717 atags = btags = ""
2025-07-01 03:02:08.722 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:08.726 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:08.731 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:08.735 if tag == 'replace':
2025-07-01 03:02:08.740 atags += '^' * la
2025-07-01 03:02:08.744 btags += '^' * lb
2025-07-01 03:02:08.749 elif tag == 'delete':
2025-07-01 03:02:08.754 atags += '-' * la
2025-07-01 03:02:08.759 elif tag == 'insert':
2025-07-01 03:02:08.763 btags += '+' * lb
2025-07-01 03:02:08.768 elif tag == 'equal':
2025-07-01 03:02:08.772 atags += ' ' * la
2025-07-01 03:02:08.777 btags += ' ' * lb
2025-07-01 03:02:08.781 else:
2025-07-01 03:02:08.786 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:08.790 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:08.795 else:
2025-07-01 03:02:08.799 # the synch pair is identical
2025-07-01 03:02:08.804 yield '  ' + aelt
2025-07-01 03:02:08.808
2025-07-01 03:02:08.813 # pump out diffs from after the synch point
2025-07-01 03:02:08.817 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:08.822
2025-07-01 03:02:08.826 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:08.831 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:08.835
2025-07-01 03:02:08.840 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:08.844 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:08.849 alo = 202, ahi = 1101
2025-07-01 03:02:08.854 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:08.858 blo = 202, bhi = 1101
2025-07-01 03:02:08.862
2025-07-01 03:02:08.867 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:08.871 g = []
2025-07-01 03:02:08.876 if alo < ahi:
2025-07-01 03:02:08.880 if blo < bhi:
2025-07-01 03:02:08.885 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:08.889 else:
2025-07-01 03:02:08.894 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:08.898 elif blo < bhi:
2025-07-01 03:02:08.903 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:08.907
2025-07-01 03:02:08.911 >       yield from g
2025-07-01 03:02:08.916
2025-07-01 03:02:08.920 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:08.924 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:08.929
2025-07-01 03:02:08.933 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:08.938 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:08.942 alo = 202, ahi = 1101
2025-07-01 03:02:08.947 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:08.951 blo = 202, bhi = 1101
2025-07-01 03:02:08.956
2025-07-01 03:02:08.961 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:08.965 r"""
2025-07-01 03:02:08.970 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:08.974 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:08.979 synch point, and intraline difference marking is done on the
2025-07-01 03:02:08.983 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:08.988
2025-07-01 03:02:08.992 Example:
2025-07-01 03:02:08.997
2025-07-01 03:02:09.002 >>> d = Differ()
2025-07-01 03:02:09.007 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:09.012 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:09.016 >>> print(''.join(results), end="")
2025-07-01 03:02:09.021 - abcDefghiJkl
2025-07-01 03:02:09.029 + abcdefGhijkl
2025-07-01 03:02:09.038 """
2025-07-01 03:02:09.042
2025-07-01 03:02:09.047 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:09.051 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:09.055 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:09.060 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:09.066 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:09.071
2025-07-01 03:02:09.078 # search for the pair that matches best without being identical
2025-07-01 03:02:09.084 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:09.090 # on junk -- unless we have to)
2025-07-01 03:02:09.097 for j in range(blo, bhi):
2025-07-01 03:02:09.102 bj = b[j]
2025-07-01 03:02:09.108 cruncher.set_seq2(bj)
2025-07-01 03:02:09.113 for i in range(alo, ahi):
2025-07-01 03:02:09.118 ai = a[i]
2025-07-01 03:02:09.123 if ai == bj:
2025-07-01 03:02:09.128 if eqi is None:
2025-07-01 03:02:09.132 eqi, eqj = i, j
2025-07-01 03:02:09.137 continue
2025-07-01 03:02:09.141 cruncher.set_seq1(ai)
2025-07-01 03:02:09.146 # computing similarity is expensive, so use the quick
2025-07-01 03:02:09.150 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:09.155 # compares by a factor of 3.
2025-07-01 03:02:09.161 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:09.165 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:09.170 # of the computation is cached by cruncher
2025-07-01 03:02:09.174 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:09.179 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:09.183 cruncher.ratio() > best_ratio:
2025-07-01 03:02:09.189 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:09.194 if best_ratio < cutoff:
2025-07-01 03:02:09.199 # no non-identical "pretty close" pair
2025-07-01 03:02:09.204 if eqi is None:
2025-07-01 03:02:09.209 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:09.214 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:09.218 return
2025-07-01 03:02:09.223 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:09.227 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:09.231 else:
2025-07-01 03:02:09.236 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:09.240 eqi = None
2025-07-01 03:02:09.245
2025-07-01 03:02:09.249 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:09.254 # identical
2025-07-01 03:02:09.258
2025-07-01 03:02:09.262 # pump out diffs from before the synch point
2025-07-01 03:02:09.267 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:09.271
2025-07-01 03:02:09.276 # do intraline marking on the synch pair
2025-07-01 03:02:09.280 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:09.285 if eqi is None:
2025-07-01 03:02:09.289 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:09.294 atags = btags = ""
2025-07-01 03:02:09.298 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:09.304 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:09.309 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:09.313 if tag == 'replace':
2025-07-01 03:02:09.318 atags += '^' * la
2025-07-01 03:02:09.322 btags += '^' * lb
2025-07-01 03:02:09.327 elif tag == 'delete':
2025-07-01 03:02:09.332 atags += '-' * la
2025-07-01 03:02:09.336 elif tag == 'insert':
2025-07-01 03:02:09.342 btags += '+' * lb
2025-07-01 03:02:09.347 elif tag == 'equal':
2025-07-01 03:02:09.351 atags += ' ' * la
2025-07-01 03:02:09.356 btags += ' ' * lb
2025-07-01 03:02:09.360 else:
2025-07-01 03:02:09.365 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:09.370 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:09.375 else:
2025-07-01 03:02:09.380 # the synch pair is identical
2025-07-01 03:02:09.384 yield '  ' + aelt
2025-07-01 03:02:09.389
2025-07-01 03:02:09.394 # pump out diffs from after the synch point
2025-07-01 03:02:09.398 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:09.403
2025-07-01 03:02:09.407 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:09.413 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:09.417
2025-07-01 03:02:09.422 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:09.426 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:09.431 alo = 203, ahi = 1101
2025-07-01 03:02:09.436 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:09.440 blo = 203, bhi = 1101
2025-07-01 03:02:09.444
2025-07-01 03:02:09.449 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:09.454 g = []
2025-07-01 03:02:09.459 if alo < ahi:
2025-07-01 03:02:09.463 if blo < bhi:
2025-07-01 03:02:09.468 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:09.472 else:
2025-07-01 03:02:09.477 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:09.481 elif blo < bhi:
2025-07-01 03:02:09.485 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:09.489
2025-07-01 03:02:09.494 >       yield from g
2025-07-01 03:02:09.498
2025-07-01 03:02:09.502 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:09.507 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:09.511
2025-07-01 03:02:09.515 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:09.520 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:09.524 alo = 203, ahi = 1101
2025-07-01 03:02:09.529 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:09.534 blo = 203, bhi = 1101
2025-07-01 03:02:09.538
2025-07-01 03:02:09.543 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:09.547 r"""
2025-07-01 03:02:09.551 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:09.556 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:09.561 synch point, and intraline difference marking is done on the
2025-07-01 03:02:09.565 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:09.570
2025-07-01 03:02:09.574 Example:
2025-07-01 03:02:09.578
2025-07-01 03:02:09.583 >>> d = Differ()
2025-07-01 03:02:09.587 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:09.592 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:09.597 >>> print(''.join(results), end="")
2025-07-01 03:02:09.601 - abcDefghiJkl
2025-07-01 03:02:09.610 + abcdefGhijkl
2025-07-01 03:02:09.619 """
2025-07-01 03:02:09.623
2025-07-01 03:02:09.628 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:09.633 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:09.637 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:09.642 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:09.646 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:09.651
2025-07-01 03:02:09.656 # search for the pair that matches best without being identical
2025-07-01 03:02:09.661 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:09.665 # on junk -- unless we have to)
2025-07-01 03:02:09.670 for j in range(blo, bhi):
2025-07-01 03:02:09.675 bj = b[j]
2025-07-01 03:02:09.680 cruncher.set_seq2(bj)
2025-07-01 03:02:09.685 for i in range(alo, ahi):
2025-07-01 03:02:09.689 ai = a[i]
2025-07-01 03:02:09.694 if ai == bj:
2025-07-01 03:02:09.699 if eqi is None:
2025-07-01 03:02:09.703 eqi, eqj = i, j
2025-07-01 03:02:09.708 continue
2025-07-01 03:02:09.712 cruncher.set_seq1(ai)
2025-07-01 03:02:09.717 # computing similarity is expensive, so use the quick
2025-07-01 03:02:09.721 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:09.733 # compares by a factor of 3.
2025-07-01 03:02:09.741 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:09.747 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:09.753 # of the computation is cached by cruncher
2025-07-01 03:02:09.758 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:09.763 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:09.767 cruncher.ratio() > best_ratio:
2025-07-01 03:02:09.772 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:09.776 if best_ratio < cutoff:
2025-07-01 03:02:09.781 # no non-identical "pretty close" pair
2025-07-01 03:02:09.785 if eqi is None:
2025-07-01 03:02:09.790 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:09.794 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:09.799 return
2025-07-01 03:02:09.803 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:09.809 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:09.813 else:
2025-07-01 03:02:09.818 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:09.822 eqi = None
2025-07-01 03:02:09.827
2025-07-01 03:02:09.832 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:09.837 # identical
2025-07-01 03:02:09.841
2025-07-01 03:02:09.846 # pump out diffs from before the synch point
2025-07-01 03:02:09.850 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:09.855
2025-07-01 03:02:09.859 # do intraline marking on the synch pair
2025-07-01 03:02:09.864 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:09.868 if eqi is None:
2025-07-01 03:02:09.873 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:09.878 atags = btags = ""
2025-07-01 03:02:09.882 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:09.887 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:09.892 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:09.897 if tag == 'replace':
2025-07-01 03:02:09.902 atags += '^' * la
2025-07-01 03:02:09.908 btags += '^' * lb
2025-07-01 03:02:09.913 elif tag == 'delete':
2025-07-01 03:02:09.917 atags += '-' * la
2025-07-01 03:02:09.922 elif tag == 'insert':
2025-07-01 03:02:09.928 btags += '+' * lb
2025-07-01 03:02:09.933 elif tag == 'equal':
2025-07-01 03:02:09.938 atags += ' ' * la
2025-07-01 03:02:09.944 btags += ' ' * lb
2025-07-01 03:02:09.948 else:
2025-07-01 03:02:09.953 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:09.957 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:09.961 else:
2025-07-01 03:02:09.966 # the synch pair is identical
2025-07-01 03:02:09.971 yield '  ' + aelt
2025-07-01 03:02:09.975
2025-07-01 03:02:09.979 # pump out diffs from after the synch point
2025-07-01 03:02:09.984 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:09.988
2025-07-01 03:02:09.993 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:09.997 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:10.001
2025-07-01 03:02:10.006 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:10.011 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:10.016 alo = 204, ahi = 1101
2025-07-01 03:02:10.022 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:10.028 blo = 204, bhi = 1101
2025-07-01 03:02:10.033
2025-07-01 03:02:10.040 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:10.045 g = []
2025-07-01 03:02:10.050 if alo < ahi:
2025-07-01 03:02:10.055 if blo < bhi:
2025-07-01 03:02:10.059 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:10.064 else:
2025-07-01 03:02:10.068 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:10.073 elif blo < bhi:
2025-07-01 03:02:10.077 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:10.081
2025-07-01 03:02:10.086 >       yield from g
2025-07-01 03:02:10.090
2025-07-01 03:02:10.095 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:10.100 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:10.105
2025-07-01 03:02:10.111 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:10.117 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:10.122 alo = 204, ahi = 1101
2025-07-01 03:02:10.127 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:10.133 blo = 204, bhi = 1101
2025-07-01 03:02:10.138
2025-07-01 03:02:10.144 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:10.149 r"""
2025-07-01 03:02:10.153 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:10.158 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:10.164 synch point, and intraline difference marking is done on the
2025-07-01 03:02:10.169 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:10.174
2025-07-01 03:02:10.179 Example:
2025-07-01 03:02:10.185
2025-07-01 03:02:10.189 >>> d = Differ()
2025-07-01 03:02:10.194 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:10.199 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:10.204 >>> print(''.join(results), end="")
2025-07-01 03:02:10.210 - abcDefghiJkl
2025-07-01 03:02:10.220 + abcdefGhijkl
2025-07-01 03:02:10.231 """
2025-07-01 03:02:10.236
2025-07-01 03:02:10.240 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:10.246 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:10.251 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:10.257 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:10.262 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:10.267
2025-07-01 03:02:10.272 # search for the pair that matches best without being identical
2025-07-01 03:02:10.278 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:10.283 # on junk -- unless we have to)
2025-07-01 03:02:10.289 for j in range(blo, bhi):
2025-07-01 03:02:10.294 bj = b[j]
2025-07-01 03:02:10.300 cruncher.set_seq2(bj)
2025-07-01 03:02:10.305 for i in range(alo, ahi):
2025-07-01 03:02:10.309 ai = a[i]
2025-07-01 03:02:10.315 if ai == bj:
2025-07-01 03:02:10.320 if eqi is None:
2025-07-01 03:02:10.325 eqi, eqj = i, j
2025-07-01 03:02:10.330 continue
2025-07-01 03:02:10.335 cruncher.set_seq1(ai)
2025-07-01 03:02:10.341 # computing similarity is expensive, so use the quick
2025-07-01 03:02:10.346 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:10.350 # compares by a factor of 3.
2025-07-01 03:02:10.355 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:10.359 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:10.364 # of the computation is cached by cruncher
2025-07-01 03:02:10.368 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:10.373 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:10.378 cruncher.ratio() > best_ratio:
2025-07-01 03:02:10.382 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:10.387 if best_ratio < cutoff:
2025-07-01 03:02:10.392 # no non-identical "pretty close" pair
2025-07-01 03:02:10.396 if eqi is None:
2025-07-01 03:02:10.401 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:10.405 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:10.410 return
2025-07-01 03:02:10.414 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:10.419 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:10.423 else:
2025-07-01 03:02:10.427 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:10.432 eqi = None
2025-07-01 03:02:10.437
2025-07-01 03:02:10.442 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:10.446 # identical
2025-07-01 03:02:10.451
2025-07-01 03:02:10.455 # pump out diffs from before the synch point
2025-07-01 03:02:10.460 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:10.464
2025-07-01 03:02:10.469 # do intraline marking on the synch pair
2025-07-01 03:02:10.473 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:10.478 if eqi is None:
2025-07-01 03:02:10.483 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:10.488 atags = btags = ""
2025-07-01 03:02:10.493 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:10.498 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:10.502 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:10.507 if tag == 'replace':
2025-07-01 03:02:10.512 atags += '^' * la
2025-07-01 03:02:10.517 btags += '^' * lb
2025-07-01 03:02:10.522 elif tag == 'delete':
2025-07-01 03:02:10.526 atags += '-' * la
2025-07-01 03:02:10.531 elif tag == 'insert':
2025-07-01 03:02:10.535 btags += '+' * lb
2025-07-01 03:02:10.540 elif tag == 'equal':
2025-07-01 03:02:10.544 atags += ' ' * la
2025-07-01 03:02:10.550 btags += ' ' * lb
2025-07-01 03:02:10.554 else:
2025-07-01 03:02:10.559 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:10.563 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:10.568 else:
2025-07-01 03:02:10.573 # the synch pair is identical
2025-07-01 03:02:10.577 yield '  ' + aelt
2025-07-01 03:02:10.582
2025-07-01 03:02:10.586 # pump out diffs from after the synch point
2025-07-01 03:02:10.591 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:10.595
2025-07-01 03:02:10.599 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:10.604 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:10.608
2025-07-01 03:02:10.612 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:10.617 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:10.621 alo = 205, ahi = 1101
2025-07-01 03:02:10.626 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:10.630 blo = 205, bhi = 1101
2025-07-01 03:02:10.635
2025-07-01 03:02:10.639 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:10.643 g = []
2025-07-01 03:02:10.647 if alo < ahi:
2025-07-01 03:02:10.653 if blo < bhi:
2025-07-01 03:02:10.658 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:10.662 else:
2025-07-01 03:02:10.667 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:10.671 elif blo < bhi:
2025-07-01 03:02:10.676 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:10.681
2025-07-01 03:02:10.685 >       yield from g
2025-07-01 03:02:10.690
2025-07-01 03:02:10.696 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:10.702 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:10.708
2025-07-01 03:02:10.713 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:10.717 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:10.722 alo = 205, ahi = 1101
2025-07-01 03:02:10.727 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:10.731 blo = 205, bhi = 1101
2025-07-01 03:02:10.735
2025-07-01 03:02:10.740 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:10.744 r"""
2025-07-01 03:02:10.749 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:10.753 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:10.758 synch point, and intraline difference marking is done on the
2025-07-01 03:02:10.763 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:10.767
2025-07-01 03:02:10.772 Example:
2025-07-01 03:02:10.776
2025-07-01 03:02:10.781 >>> d = Differ()
2025-07-01 03:02:10.785 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:10.790 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:10.794 >>> print(''.join(results), end="")
2025-07-01 03:02:10.798 - abcDefghiJkl
2025-07-01 03:02:10.807 + abcdefGhijkl
2025-07-01 03:02:10.818 """
2025-07-01 03:02:10.824
2025-07-01 03:02:10.830 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:10.835 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:10.840 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:10.845 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:10.851 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:10.856
2025-07-01 03:02:10.862 # search for the pair that matches best without being identical
2025-07-01 03:02:10.867 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:10.871 # on junk -- unless we have to)
2025-07-01 03:02:10.876 for j in range(blo, bhi):
2025-07-01 03:02:10.882 bj = b[j]
2025-07-01 03:02:10.887 cruncher.set_seq2(bj)
2025-07-01 03:02:10.893 for i in range(alo, ahi):
2025-07-01 03:02:10.898 ai = a[i]
2025-07-01 03:02:10.903 if ai == bj:
2025-07-01 03:02:10.908 if eqi is None:
2025-07-01 03:02:10.913 eqi, eqj = i, j
2025-07-01 03:02:10.919 continue
2025-07-01 03:02:10.924 cruncher.set_seq1(ai)
2025-07-01 03:02:10.929 # computing similarity is expensive, so use the quick
2025-07-01 03:02:10.935 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:10.942 # compares by a factor of 3.
2025-07-01 03:02:10.947 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:10.952 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:10.957 # of the computation is cached by cruncher
2025-07-01 03:02:10.963 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:10.968 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:10.972 cruncher.ratio() > best_ratio:
2025-07-01 03:02:10.978 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:10.983 if best_ratio < cutoff:
2025-07-01 03:02:10.987 # no non-identical "pretty close" pair
2025-07-01 03:02:10.992 if eqi is None:
2025-07-01 03:02:10.996 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:11.002 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:11.006 return
2025-07-01 03:02:11.011 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:11.017 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:11.022 else:
2025-07-01 03:02:11.027 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:11.032 eqi = None
2025-07-01 03:02:11.037
2025-07-01 03:02:11.041 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:11.046 # identical
2025-07-01 03:02:11.050
2025-07-01 03:02:11.054 # pump out diffs from before the synch point
2025-07-01 03:02:11.059 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:11.063
2025-07-01 03:02:11.068 # do intraline marking on the synch pair
2025-07-01 03:02:11.072 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:11.076 if eqi is None:
2025-07-01 03:02:11.081 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:11.085 atags = btags = ""
2025-07-01 03:02:11.090 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:11.094 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:11.098 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:11.103 if tag == 'replace':
2025-07-01 03:02:11.108 atags += '^' * la
2025-07-01 03:02:11.113 btags += '^' * lb
2025-07-01 03:02:11.119 elif tag == 'delete':
2025-07-01 03:02:11.124 atags += '-' * la
2025-07-01 03:02:11.129 elif tag == 'insert':
2025-07-01 03:02:11.134 btags += '+' * lb
2025-07-01 03:02:11.144 elif tag == 'equal':
2025-07-01 03:02:11.153 atags += ' ' * la
2025-07-01 03:02:11.160 btags += ' ' * lb
2025-07-01 03:02:11.174 else:
2025-07-01 03:02:11.181 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:11.190 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:11.197 else:
2025-07-01 03:02:11.204 # the synch pair is identical
2025-07-01 03:02:11.209 yield '  ' + aelt
2025-07-01 03:02:11.215
2025-07-01 03:02:11.221 # pump out diffs from after the synch point
2025-07-01 03:02:11.226 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:11.231
2025-07-01 03:02:11.236 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:11.240 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:11.249
2025-07-01 03:02:11.254 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:11.260 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:11.265 alo = 206, ahi = 1101
2025-07-01 03:02:11.271 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:11.276 blo = 206, bhi = 1101
2025-07-01 03:02:11.280
2025-07-01 03:02:11.285 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:11.289 g = []
2025-07-01 03:02:11.294 if alo < ahi:
2025-07-01 03:02:11.298 if blo < bhi:
2025-07-01 03:02:11.306 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:11.314 else:
2025-07-01 03:02:11.321 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:11.325 elif blo < bhi:
2025-07-01 03:02:11.334 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:11.345
2025-07-01 03:02:11.350 >       yield from g
2025-07-01 03:02:11.355
2025-07-01 03:02:11.360 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:11.365 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:11.369
2025-07-01 03:02:11.374 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:11.381 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:11.386 alo = 206, ahi = 1101
2025-07-01 03:02:11.392 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:11.397 blo = 206, bhi = 1101
2025-07-01 03:02:11.402
2025-07-01 03:02:11.406 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:11.411 r"""
2025-07-01 03:02:11.415 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:11.420 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:11.425 synch point, and intraline difference marking is done on the
2025-07-01 03:02:11.430 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:11.434
2025-07-01 03:02:11.438 Example:
2025-07-01 03:02:11.443
2025-07-01 03:02:11.447 >>> d = Differ()
2025-07-01 03:02:11.452 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:11.457 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:11.461 >>> print(''.join(results), end="")
2025-07-01 03:02:11.466 - abcDefghiJkl
2025-07-01 03:02:11.475 + abcdefGhijkl
2025-07-01 03:02:11.484 """
2025-07-01 03:02:11.488
2025-07-01 03:02:11.493 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:11.497 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:11.502 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:11.506 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:11.511 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:11.516
2025-07-01 03:02:11.521 # search for the pair that matches best without being identical
2025-07-01 03:02:11.525 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:11.530 # on junk -- unless we have to)
2025-07-01 03:02:11.534 for j in range(blo, bhi):
2025-07-01 03:02:11.538 bj = b[j]
2025-07-01 03:02:11.543 cruncher.set_seq2(bj)
2025-07-01 03:02:11.547 for i in range(alo, ahi):
2025-07-01 03:02:11.552 ai = a[i]
2025-07-01 03:02:11.556 if ai == bj:
2025-07-01 03:02:11.561 if eqi is None:
2025-07-01 03:02:11.566 eqi, eqj = i, j
2025-07-01 03:02:11.571 continue
2025-07-01 03:02:11.575 cruncher.set_seq1(ai)
2025-07-01 03:02:11.580 # computing similarity is expensive, so use the quick
2025-07-01 03:02:11.585 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:11.590 # compares by a factor of 3.
2025-07-01 03:02:11.594 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:11.599 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:11.603 # of the computation is cached by cruncher
2025-07-01 03:02:11.608 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:11.613 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:11.618 cruncher.ratio() > best_ratio:
2025-07-01 03:02:11.622 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:11.627 if best_ratio < cutoff:
2025-07-01 03:02:11.631 # no non-identical "pretty close" pair
2025-07-01 03:02:11.636 if eqi is None:
2025-07-01 03:02:11.640 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:11.645 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:11.649 return
2025-07-01 03:02:11.654 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:11.658 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:11.663 else:
2025-07-01 03:02:11.667 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:11.672 eqi = None
2025-07-01 03:02:11.676
2025-07-01 03:02:11.681 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:11.686 # identical
2025-07-01 03:02:11.691
2025-07-01 03:02:11.696 # pump out diffs from before the synch point
2025-07-01 03:02:11.700 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:11.705
2025-07-01 03:02:11.710 # do intraline marking on the synch pair
2025-07-01 03:02:11.714 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:11.719 if eqi is None:
2025-07-01 03:02:11.723 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:11.727 atags = btags = ""
2025-07-01 03:02:11.732 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:11.736 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:11.741 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:11.745 if tag == 'replace':
2025-07-01 03:02:11.750 atags += '^' * la
2025-07-01 03:02:11.754 btags += '^' * lb
2025-07-01 03:02:11.759 elif tag == 'delete':
2025-07-01 03:02:11.763 atags += '-' * la
2025-07-01 03:02:11.768 elif tag == 'insert':
2025-07-01 03:02:11.772 btags += '+' * lb
2025-07-01 03:02:11.778 elif tag == 'equal':
2025-07-01 03:02:11.783 atags += ' ' * la
2025-07-01 03:02:11.789 btags += ' ' * lb
2025-07-01 03:02:11.794 else:
2025-07-01 03:02:11.799 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:11.805 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:11.810 else:
2025-07-01 03:02:11.816 # the synch pair is identical
2025-07-01 03:02:11.821 yield '  ' + aelt
2025-07-01 03:02:11.826
2025-07-01 03:02:11.831 # pump out diffs from after the synch point
2025-07-01 03:02:11.836 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:11.841
2025-07-01 03:02:11.846 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:11.851 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:11.856
2025-07-01 03:02:11.862 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:11.867 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:11.872 alo = 207, ahi = 1101
2025-07-01 03:02:11.877 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:11.884 blo = 207, bhi = 1101
2025-07-01 03:02:11.889
2025-07-01 03:02:11.894 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:11.898 g = []
2025-07-01 03:02:11.904 if alo < ahi:
2025-07-01 03:02:11.908 if blo < bhi:
2025-07-01 03:02:11.913 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:11.918 else:
2025-07-01 03:02:11.924 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:11.928 elif blo < bhi:
2025-07-01 03:02:11.933 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:11.938
2025-07-01 03:02:11.942 >       yield from g
2025-07-01 03:02:11.947
2025-07-01 03:02:11.951 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:11.956 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:11.961
2025-07-01 03:02:11.966 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:11.972 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:11.977 alo = 207, ahi = 1101
2025-07-01 03:02:11.983 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:11.988 blo = 207, bhi = 1101
2025-07-01 03:02:11.993
2025-07-01 03:02:11.998 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:12.003 r"""
2025-07-01 03:02:12.009 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:12.014 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:12.019 synch point, and intraline difference marking is done on the
2025-07-01 03:02:12.025 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:12.031
2025-07-01 03:02:12.038 Example:
2025-07-01 03:02:12.045
2025-07-01 03:02:12.052 >>> d = Differ()
2025-07-01 03:02:12.059 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:12.065 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:12.071 >>> print(''.join(results), end="")
2025-07-01 03:02:12.076 - abcDefghiJkl
2025-07-01 03:02:12.086 + abcdefGhijkl
2025-07-01 03:02:12.095 """
2025-07-01 03:02:12.099
2025-07-01 03:02:12.104 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:12.108 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:12.114 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:12.118 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:12.123 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:12.128
2025-07-01 03:02:12.132 # search for the pair that matches best without being identical
2025-07-01 03:02:12.137 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:12.141 # on junk -- unless we have to)
2025-07-01 03:02:12.146 for j in range(blo, bhi):
2025-07-01 03:02:12.150 bj = b[j]
2025-07-01 03:02:12.155 cruncher.set_seq2(bj)
2025-07-01 03:02:12.159 for i in range(alo, ahi):
2025-07-01 03:02:12.163 ai = a[i]
2025-07-01 03:02:12.168 if ai == bj:
2025-07-01 03:02:12.172 if eqi is None:
2025-07-01 03:02:12.177 eqi, eqj = i, j
2025-07-01 03:02:12.181 continue
2025-07-01 03:02:12.186 cruncher.set_seq1(ai)
2025-07-01 03:02:12.190 # computing similarity is expensive, so use the quick
2025-07-01 03:02:12.195 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:12.200 # compares by a factor of 3.
2025-07-01 03:02:12.205 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:12.209 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:12.214 # of the computation is cached by cruncher
2025-07-01 03:02:12.218 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:12.223 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:12.228 cruncher.ratio() > best_ratio:
2025-07-01 03:02:12.232 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:12.236 if best_ratio < cutoff:
2025-07-01 03:02:12.241 # no non-identical "pretty close" pair
2025-07-01 03:02:12.245 if eqi is None:
2025-07-01 03:02:12.249 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:12.254 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:12.258 return
2025-07-01 03:02:12.263 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:12.267 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:12.272 else:
2025-07-01 03:02:12.276 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:12.281 eqi = None
2025-07-01 03:02:12.285
2025-07-01 03:02:12.289 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:12.294 # identical
2025-07-01 03:02:12.298
2025-07-01 03:02:12.303 # pump out diffs from before the synch point
2025-07-01 03:02:12.307 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:12.312
2025-07-01 03:02:12.316 # do intraline marking on the synch pair
2025-07-01 03:02:12.321 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:12.325 if eqi is None:
2025-07-01 03:02:12.330 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:12.335 atags = btags = ""
2025-07-01 03:02:12.339 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:12.344 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:12.349 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:12.353 if tag == 'replace':
2025-07-01 03:02:12.358 atags += '^' * la
2025-07-01 03:02:12.362 btags += '^' * lb
2025-07-01 03:02:12.367 elif tag == 'delete':
2025-07-01 03:02:12.372 atags += '-' * la
2025-07-01 03:02:12.376 elif tag == 'insert':
2025-07-01 03:02:12.381 btags += '+' * lb
2025-07-01 03:02:12.386 elif tag == 'equal':
2025-07-01 03:02:12.391 atags += ' ' * la
2025-07-01 03:02:12.395 btags += ' ' * lb
2025-07-01 03:02:12.400 else:
2025-07-01 03:02:12.404 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:12.409 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:12.413 else:
2025-07-01 03:02:12.418 # the synch pair is identical
2025-07-01 03:02:12.423 yield '  ' + aelt
2025-07-01 03:02:12.428
2025-07-01 03:02:12.432 # pump out diffs from after the synch point
2025-07-01 03:02:12.438 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:12.443
2025-07-01 03:02:12.447 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:12.452 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:12.457
2025-07-01 03:02:12.462 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:12.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:12.471 alo = 208, ahi = 1101
2025-07-01 03:02:12.476 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:12.481 blo = 208, bhi = 1101
2025-07-01 03:02:12.485
2025-07-01 03:02:12.490 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:12.494 g = []
2025-07-01 03:02:12.499 if alo < ahi:
2025-07-01 03:02:12.503 if blo < bhi:
2025-07-01 03:02:12.508 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:12.512 else:
2025-07-01 03:02:12.517 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:12.522 elif blo < bhi:
2025-07-01 03:02:12.526 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:12.531
2025-07-01 03:02:12.536 >       yield from g
2025-07-01 03:02:12.541
2025-07-01 03:02:12.546 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:12.551 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:12.556
2025-07-01 03:02:12.560 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:12.565 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:12.570 alo = 208, ahi = 1101
2025-07-01 03:02:12.575 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:12.579 blo = 208, bhi = 1101
2025-07-01 03:02:12.583
2025-07-01 03:02:12.588 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:12.593 r"""
2025-07-01 03:02:12.597 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:12.602 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:12.607 synch point, and intraline difference marking is done on the
2025-07-01 03:02:12.612 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:12.618
2025-07-01 03:02:12.624 Example:
2025-07-01 03:02:12.630
2025-07-01 03:02:12.636 >>> d = Differ()
2025-07-01 03:02:12.642 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:12.648 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:12.653 >>> print(''.join(results), end="")
2025-07-01 03:02:12.658 - abcDefghiJkl
2025-07-01 03:02:12.667 + abcdefGhijkl
2025-07-01 03:02:12.675 """
2025-07-01 03:02:12.680
2025-07-01 03:02:12.684 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:12.689 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:12.693 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:12.698 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:12.702 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:12.706
2025-07-01 03:02:12.712 # search for the pair that matches best without being identical
2025-07-01 03:02:12.717 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:12.721 # on junk -- unless we have to)
2025-07-01 03:02:12.726 for j in range(blo, bhi):
2025-07-01 03:02:12.731 bj = b[j]
2025-07-01 03:02:12.735 cruncher.set_seq2(bj)
2025-07-01 03:02:12.740 for i in range(alo, ahi):
2025-07-01 03:02:12.745 ai = a[i]
2025-07-01 03:02:12.749 if ai == bj:
2025-07-01 03:02:12.753 if eqi is None:
2025-07-01 03:02:12.758 eqi, eqj = i, j
2025-07-01 03:02:12.763 continue
2025-07-01 03:02:12.769 cruncher.set_seq1(ai)
2025-07-01 03:02:12.773 # computing similarity is expensive, so use the quick
2025-07-01 03:02:12.778 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:12.783 # compares by a factor of 3.
2025-07-01 03:02:12.788 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:12.794 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:12.799 # of the computation is cached by cruncher
2025-07-01 03:02:12.803 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:12.808 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:12.812 cruncher.ratio() > best_ratio:
2025-07-01 03:02:12.817 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:12.821 if best_ratio < cutoff:
2025-07-01 03:02:12.825 # no non-identical "pretty close" pair
2025-07-01 03:02:12.830 if eqi is None:
2025-07-01 03:02:12.834 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:12.839 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:12.843 return
2025-07-01 03:02:12.847 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:12.852 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:12.856 else:
2025-07-01 03:02:12.861 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:12.865 eqi = None
2025-07-01 03:02:12.870
2025-07-01 03:02:12.874 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:12.879 # identical
2025-07-01 03:02:12.883
2025-07-01 03:02:12.888 # pump out diffs from before the synch point
2025-07-01 03:02:12.892 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:12.896
2025-07-01 03:02:12.901 # do intraline marking on the synch pair
2025-07-01 03:02:12.905 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:12.910 if eqi is None:
2025-07-01 03:02:12.914 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:12.919 atags = btags = ""
2025-07-01 03:02:12.923 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:12.928 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:12.932 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:12.937 if tag == 'replace':
2025-07-01 03:02:12.942 atags += '^' * la
2025-07-01 03:02:12.946 btags += '^' * lb
2025-07-01 03:02:12.951 elif tag == 'delete':
2025-07-01 03:02:12.955 atags += '-' * la
2025-07-01 03:02:12.960 elif tag == 'insert':
2025-07-01 03:02:12.965 btags += '+' * lb
2025-07-01 03:02:12.969 elif tag == 'equal':
2025-07-01 03:02:12.974 atags += ' ' * la
2025-07-01 03:02:12.978 btags += ' ' * lb
2025-07-01 03:02:12.983 else:
2025-07-01 03:02:12.988 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:12.993 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:12.998 else:
2025-07-01 03:02:13.002 # the synch pair is identical
2025-07-01 03:02:13.007 yield '  ' + aelt
2025-07-01 03:02:13.012
2025-07-01 03:02:13.017 # pump out diffs from after the synch point
2025-07-01 03:02:13.021 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:13.026
2025-07-01 03:02:13.030 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:13.035 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:13.039
2025-07-01 03:02:13.044 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:13.049 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:13.053 alo = 209, ahi = 1101
2025-07-01 03:02:13.058 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:13.063 blo = 209, bhi = 1101
2025-07-01 03:02:13.067
2025-07-01 03:02:13.071 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:13.076 g = []
2025-07-01 03:02:13.080 if alo < ahi:
2025-07-01 03:02:13.085 if blo < bhi:
2025-07-01 03:02:13.089 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:13.093 else:
2025-07-01 03:02:13.098 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:13.102 elif blo < bhi:
2025-07-01 03:02:13.107 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:13.111
2025-07-01 03:02:13.115 >       yield from g
2025-07-01 03:02:13.120
2025-07-01 03:02:13.124 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:13.128 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:13.133
2025-07-01 03:02:13.137 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:13.142 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:13.146 alo = 209, ahi = 1101
2025-07-01 03:02:13.151 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:13.155 blo = 209, bhi = 1101
2025-07-01 03:02:13.160
2025-07-01 03:02:13.164 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:13.168 r"""
2025-07-01 03:02:13.173 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:13.178 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:13.183 synch point, and intraline difference marking is done on the
2025-07-01 03:02:13.187 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:13.191
2025-07-01 03:02:13.195 Example:
2025-07-01 03:02:13.200
2025-07-01 03:02:13.204 >>> d = Differ()
2025-07-01 03:02:13.208 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:13.213 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:13.217 >>> print(''.join(results), end="")
2025-07-01 03:02:13.221 - abcDefghiJkl
2025-07-01 03:02:13.230 + abcdefGhijkl
2025-07-01 03:02:13.239 """
2025-07-01 03:02:13.244
2025-07-01 03:02:13.248 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:13.253 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:13.257 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:13.262 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:13.266 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:13.270
2025-07-01 03:02:13.275 # search for the pair that matches best without being identical
2025-07-01 03:02:13.279 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:13.283 # on junk -- unless we have to)
2025-07-01 03:02:13.288 for j in range(blo, bhi):
2025-07-01 03:02:13.292 bj = b[j]
2025-07-01 03:02:13.296 cruncher.set_seq2(bj)
2025-07-01 03:02:13.301 for i in range(alo, ahi):
2025-07-01 03:02:13.305 ai = a[i]
2025-07-01 03:02:13.309 if ai == bj:
2025-07-01 03:02:13.314 if eqi is None:
2025-07-01 03:02:13.318 eqi, eqj = i, j
2025-07-01 03:02:13.322 continue
2025-07-01 03:02:13.326 cruncher.set_seq1(ai)
2025-07-01 03:02:13.331 # computing similarity is expensive, so use the quick
2025-07-01 03:02:13.335 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:13.339 # compares by a factor of 3.
2025-07-01 03:02:13.344 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:13.349 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:13.353 # of the computation is cached by cruncher
2025-07-01 03:02:13.358 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:13.362 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:13.367 cruncher.ratio() > best_ratio:
2025-07-01 03:02:13.371 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:13.376 if best_ratio < cutoff:
2025-07-01 03:02:13.380 # no non-identical "pretty close" pair
2025-07-01 03:02:13.384 if eqi is None:
2025-07-01 03:02:13.389 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:13.393 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:13.398 return
2025-07-01 03:02:13.402 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:13.407 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:13.411 else:
2025-07-01 03:02:13.416 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:13.420 eqi = None
2025-07-01 03:02:13.424
2025-07-01 03:02:13.428 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:13.433 # identical
2025-07-01 03:02:13.438
2025-07-01 03:02:13.442 # pump out diffs from before the synch point
2025-07-01 03:02:13.446 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:13.451
2025-07-01 03:02:13.455 # do intraline marking on the synch pair
2025-07-01 03:02:13.459 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:13.463 if eqi is None:
2025-07-01 03:02:13.468 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:13.472 atags = btags = ""
2025-07-01 03:02:13.477 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:13.481 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:13.485 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:13.490 if tag == 'replace':
2025-07-01 03:02:13.494 atags += '^' * la
2025-07-01 03:02:13.499 btags += '^' * lb
2025-07-01 03:02:13.504 elif tag == 'delete':
2025-07-01 03:02:13.509 atags += '-' * la
2025-07-01 03:02:13.514 elif tag == 'insert':
2025-07-01 03:02:13.518 btags += '+' * lb
2025-07-01 03:02:13.522 elif tag == 'equal':
2025-07-01 03:02:13.527 atags += ' ' * la
2025-07-01 03:02:13.531 btags += ' ' * lb
2025-07-01 03:02:13.536 else:
2025-07-01 03:02:13.540 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:13.545 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:13.549 else:
2025-07-01 03:02:13.554 # the synch pair is identical
2025-07-01 03:02:13.559 yield '  ' + aelt
2025-07-01 03:02:13.564
2025-07-01 03:02:13.568 # pump out diffs from after the synch point
2025-07-01 03:02:13.573 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:13.578
2025-07-01 03:02:13.582 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:13.587 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:13.592
2025-07-01 03:02:13.596 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:13.602 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:13.606 alo = 210, ahi = 1101
2025-07-01 03:02:13.611 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:13.615 blo = 210, bhi = 1101
2025-07-01 03:02:13.619
2025-07-01 03:02:13.624 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:13.628 g = []
2025-07-01 03:02:13.633 if alo < ahi:
2025-07-01 03:02:13.637 if blo < bhi:
2025-07-01 03:02:13.641 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:13.646 else:
2025-07-01 03:02:13.650 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:13.654 elif blo < bhi:
2025-07-01 03:02:13.659 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:13.664
2025-07-01 03:02:13.668 >       yield from g
2025-07-01 03:02:13.673
2025-07-01 03:02:13.677 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:13.682 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:13.686
2025-07-01 03:02:13.691 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:13.697 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:13.701 alo = 210, ahi = 1101
2025-07-01 03:02:13.716 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:13.730 blo = 210, bhi = 1101
2025-07-01 03:02:13.742
2025-07-01 03:02:13.754 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:13.766 r"""
2025-07-01 03:02:13.774 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:13.788 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:13.806 synch point, and intraline difference marking is done on the
2025-07-01 03:02:13.818 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:13.826
2025-07-01 03:02:13.838 Example:
2025-07-01 03:02:13.850
2025-07-01 03:02:13.858 >>> d = Differ()
2025-07-01 03:02:13.874 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:13.882 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:13.896 >>> print(''.join(results), end="")
2025-07-01 03:02:13.909 - abcDefghiJkl
2025-07-01 03:02:13.933 + abcdefGhijkl
2025-07-01 03:02:13.958 """
2025-07-01 03:02:13.965
2025-07-01 03:02:13.978 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:13.997 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:14.006 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:14.019 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:14.035 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:14.053
2025-07-01 03:02:14.072 # search for the pair that matches best without being identical
2025-07-01 03:02:14.089 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:14.104 # on junk -- unless we have to)
2025-07-01 03:02:14.125 for j in range(blo, bhi):
2025-07-01 03:02:14.141 bj = b[j]
2025-07-01 03:02:14.154 cruncher.set_seq2(bj)
2025-07-01 03:02:14.169 for i in range(alo, ahi):
2025-07-01 03:02:14.185 ai = a[i]
2025-07-01 03:02:14.204 if ai == bj:
2025-07-01 03:02:14.213 if eqi is None:
2025-07-01 03:02:14.226 eqi, eqj = i, j
2025-07-01 03:02:14.238 continue
2025-07-01 03:02:14.246 cruncher.set_seq1(ai)
2025-07-01 03:02:14.257 # computing similarity is expensive, so use the quick
2025-07-01 03:02:14.270 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:14.278 # compares by a factor of 3.
2025-07-01 03:02:14.290 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:14.301 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:14.314 # of the computation is cached by cruncher
2025-07-01 03:02:14.324 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:14.346 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:14.354 cruncher.ratio() > best_ratio:
2025-07-01 03:02:14.362 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:14.374 if best_ratio < cutoff:
2025-07-01 03:02:14.386 # no non-identical "pretty close" pair
2025-07-01 03:02:14.394 if eqi is None:
2025-07-01 03:02:14.406 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:14.422 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:14.430 return
2025-07-01 03:02:14.442 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:14.450 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:14.462 else:
2025-07-01 03:02:14.470 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:14.482 eqi = None
2025-07-01 03:02:14.494
2025-07-01 03:02:14.506 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:14.518 # identical
2025-07-01 03:02:14.526
2025-07-01 03:02:14.533 # pump out diffs from before the synch point
2025-07-01 03:02:14.545 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:14.558
2025-07-01 03:02:14.570 # do intraline marking on the synch pair
2025-07-01 03:02:14.577 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:14.590 if eqi is None:
2025-07-01 03:02:14.598 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:14.610 atags = btags = ""
2025-07-01 03:02:14.617 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:14.630 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:14.638 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:14.650 if tag == 'replace':
2025-07-01 03:02:14.661 atags += '^' * la
2025-07-01 03:02:14.674 btags += '^' * lb
2025-07-01 03:02:14.682 elif tag == 'delete':
2025-07-01 03:02:14.696 atags += '-' * la
2025-07-01 03:02:14.706 elif tag == 'insert':
2025-07-01 03:02:14.718 btags += '+' * lb
2025-07-01 03:02:14.730 elif tag == 'equal':
2025-07-01 03:02:14.738 atags += ' ' * la
2025-07-01 03:02:14.750 btags += ' ' * lb
2025-07-01 03:02:14.762 else:
2025-07-01 03:02:14.772 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:14.781 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:14.794 else:
2025-07-01 03:02:14.802 # the synch pair is identical
2025-07-01 03:02:14.815 yield '  ' + aelt
2025-07-01 03:02:14.828
2025-07-01 03:02:14.838 # pump out diffs from after the synch point
2025-07-01 03:02:14.854 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:14.862
2025-07-01 03:02:14.874 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:14.882 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:14.894
2025-07-01 03:02:14.906 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:14.922 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:14.933 alo = 211, ahi = 1101
2025-07-01 03:02:14.950 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:14.958 blo = 211, bhi = 1101
2025-07-01 03:02:14.975
2025-07-01 03:02:14.990 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:15.003 g = []
2025-07-01 03:02:15.018 if alo < ahi:
2025-07-01 03:02:15.026 if blo < bhi:
2025-07-01 03:02:15.038 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:15.050 else:
2025-07-01 03:02:15.066 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:15.076 elif blo < bhi:
2025-07-01 03:02:15.086 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:15.098
2025-07-01 03:02:15.108 >       yield from g
2025-07-01 03:02:15.126
2025-07-01 03:02:15.134 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:15.146 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:15.154
2025-07-01 03:02:15.166 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:15.174 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:15.190 alo = 211, ahi = 1101
2025-07-01 03:02:15.197 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:15.205 blo = 211, bhi = 1101
2025-07-01 03:02:15.212
2025-07-01 03:02:15.228 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:15.238 r"""
2025-07-01 03:02:15.250 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:15.258 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:15.271 synch point, and intraline difference marking is done on the
2025-07-01 03:02:15.281 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:15.298
2025-07-01 03:02:15.306 Example:
2025-07-01 03:02:15.318
2025-07-01 03:02:15.334 >>> d = Differ()
2025-07-01 03:02:15.342 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:15.354 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:15.370 >>> print(''.join(results), end="")
2025-07-01 03:02:15.382 - abcDefghiJkl
2025-07-01 03:02:15.406 + abcdefGhijkl
2025-07-01 03:02:15.426 """
2025-07-01 03:02:15.442
2025-07-01 03:02:15.450 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:15.462 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:15.470 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:15.482 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:15.490 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:15.502
2025-07-01 03:02:15.518 # search for the pair that matches best without being identical
2025-07-01 03:02:15.526 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:15.540 # on junk -- unless we have to)
2025-07-01 03:02:15.554 for j in range(blo, bhi):
2025-07-01 03:02:15.561 bj = b[j]
2025-07-01 03:02:15.574 cruncher.set_seq2(bj)
2025-07-01 03:02:15.586 for i in range(alo, ahi):
2025-07-01 03:02:15.593 ai = a[i]
2025-07-01 03:02:15.606 if ai == bj:
2025-07-01 03:02:15.615 if eqi is None:
2025-07-01 03:02:15.630 eqi, eqj = i, j
2025-07-01 03:02:15.638 continue
2025-07-01 03:02:15.654 cruncher.set_seq1(ai)
2025-07-01 03:02:15.666 # computing similarity is expensive, so use the quick
2025-07-01 03:02:15.682 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:15.689 # compares by a factor of 3.
2025-07-01 03:02:15.706 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:15.714 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:15.726 # of the computation is cached by cruncher
2025-07-01 03:02:15.738 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:15.746 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:15.762 cruncher.ratio() > best_ratio:
2025-07-01 03:02:15.770 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:15.786 if best_ratio < cutoff:
2025-07-01 03:02:15.801 # no non-identical "pretty close" pair
2025-07-01 03:02:15.810 if eqi is None:
2025-07-01 03:02:15.822 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:15.829 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:15.838 return
2025-07-01 03:02:15.850 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:15.859 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:15.870 else:
2025-07-01 03:02:15.885 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:15.893 eqi = None
2025-07-01 03:02:15.900
2025-07-01 03:02:15.907 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:15.912 # identical
2025-07-01 03:02:15.917
2025-07-01 03:02:15.924 # pump out diffs from before the synch point
2025-07-01 03:02:15.930 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:15.935
2025-07-01 03:02:15.941 # do intraline marking on the synch pair
2025-07-01 03:02:15.947 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:15.953 if eqi is None:
2025-07-01 03:02:15.959 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:15.967 atags = btags = ""
2025-07-01 03:02:15.974 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:15.978 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:15.986 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:15.993 if tag == 'replace':
2025-07-01 03:02:16.005 atags += '^' * la
2025-07-01 03:02:16.015 btags += '^' * lb
2025-07-01 03:02:16.020 elif tag == 'delete':
2025-07-01 03:02:16.025 atags += '-' * la
2025-07-01 03:02:16.030 elif tag == 'insert':
2025-07-01 03:02:16.038 btags += '+' * lb
2025-07-01 03:02:16.058 elif tag == 'equal':
2025-07-01 03:02:16.070 atags += ' ' * la
2025-07-01 03:02:16.082 btags += ' ' * lb
2025-07-01 03:02:16.090 else:
2025-07-01 03:02:16.098 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:16.114 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:16.122 else:
2025-07-01 03:02:16.134 # the synch pair is identical
2025-07-01 03:02:16.142 yield '  ' + aelt
2025-07-01 03:02:16.157
2025-07-01 03:02:16.170 # pump out diffs from after the synch point
2025-07-01 03:02:16.178 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:16.190
2025-07-01 03:02:16.202 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:16.210 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:16.222
2025-07-01 03:02:16.230 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:16.246 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:16.254 alo = 212, ahi = 1101
2025-07-01 03:02:16.270 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:16.278 blo = 212, bhi = 1101
2025-07-01 03:02:16.290
2025-07-01 03:02:16.302 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:16.310 g = []
2025-07-01 03:02:16.318 if alo < ahi:
2025-07-01 03:02:16.334 if blo < bhi:
2025-07-01 03:02:16.342 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:16.354 else:
2025-07-01 03:02:16.362 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:16.372 elif blo < bhi:
2025-07-01 03:02:16.377 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:16.382
2025-07-01 03:02:16.388 >       yield from g
2025-07-01 03:02:16.393
2025-07-01 03:02:16.398 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:16.403 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:16.408
2025-07-01 03:02:16.414 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:16.422 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:16.428 alo = 212, ahi = 1101
2025-07-01 03:02:16.434 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:16.438 blo = 212, bhi = 1101
2025-07-01 03:02:16.442
2025-07-01 03:02:16.447 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:16.454 r"""
2025-07-01 03:02:16.467 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:16.480 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:16.488 synch point, and intraline difference marking is done on the
2025-07-01 03:02:16.501 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:16.512
2025-07-01 03:02:16.526 Example:
2025-07-01 03:02:16.544
2025-07-01 03:02:16.553 >>> d = Differ()
2025-07-01 03:02:16.569 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:16.579 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:16.590 >>> print(''.join(results), end="")
2025-07-01 03:02:16.606 - abcDefghiJkl
2025-07-01 03:02:16.626 + abcdefGhijkl
2025-07-01 03:02:16.659 """
2025-07-01 03:02:16.674
2025-07-01 03:02:16.682 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:16.694 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:16.702 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:16.714 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:16.722 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:16.734
2025-07-01 03:02:16.742 # search for the pair that matches best without being identical
2025-07-01 03:02:16.754 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:16.766 # on junk -- unless we have to)
2025-07-01 03:02:16.778 for j in range(blo, bhi):
2025-07-01 03:02:16.790 bj = b[j]
2025-07-01 03:02:16.798 cruncher.set_seq2(bj)
2025-07-01 03:02:16.810 for i in range(alo, ahi):
2025-07-01 03:02:16.822 ai = a[i]
2025-07-01 03:02:16.830 if ai == bj:
2025-07-01 03:02:16.842 if eqi is None:
2025-07-01 03:02:16.854 eqi, eqj = i, j
2025-07-01 03:02:16.862 continue
2025-07-01 03:02:16.874 cruncher.set_seq1(ai)
2025-07-01 03:02:16.882 # computing similarity is expensive, so use the quick
2025-07-01 03:02:16.894 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:16.906 # compares by a factor of 3.
2025-07-01 03:02:16.914 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:16.926 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:16.938 # of the computation is cached by cruncher
2025-07-01 03:02:16.948 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:16.962 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:16.978 cruncher.ratio() > best_ratio:
2025-07-01 03:02:16.990 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:16.998 if best_ratio < cutoff:
2025-07-01 03:02:17.012 # no non-identical "pretty close" pair
2025-07-01 03:02:17.026 if eqi is None:
2025-07-01 03:02:17.042 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:17.058 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:17.066 return
2025-07-01 03:02:17.078 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:17.093 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:17.106 else:
2025-07-01 03:02:17.114 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:17.126 eqi = None
2025-07-01 03:02:17.138
2025-07-01 03:02:17.150 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:17.162 # identical
2025-07-01 03:02:17.174
2025-07-01 03:02:17.185 # pump out diffs from before the synch point
2025-07-01 03:02:17.198 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:17.211
2025-07-01 03:02:17.222 # do intraline marking on the synch pair
2025-07-01 03:02:17.238 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:17.246 if eqi is None:
2025-07-01 03:02:17.261 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:17.270 atags = btags = ""
2025-07-01 03:02:17.282 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:17.294 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:17.302 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:17.309 if tag == 'replace':
2025-07-01 03:02:17.324 atags += '^' * la
2025-07-01 03:02:17.339 btags += '^' * lb
2025-07-01 03:02:17.349 elif tag == 'delete':
2025-07-01 03:02:17.358 atags += '-' * la
2025-07-01 03:02:17.374 elif tag == 'insert':
2025-07-01 03:02:17.386 btags += '+' * lb
2025-07-01 03:02:17.402 elif tag == 'equal':
2025-07-01 03:02:17.417 atags += ' ' * la
2025-07-01 03:02:17.426 btags += ' ' * lb
2025-07-01 03:02:17.442 else:
2025-07-01 03:02:17.450 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:17.462 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:17.470 else:
2025-07-01 03:02:17.482 # the synch pair is identical
2025-07-01 03:02:17.495 yield '  ' + aelt
2025-07-01 03:02:17.501
2025-07-01 03:02:17.518 # pump out diffs from after the synch point
2025-07-01 03:02:17.532 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:17.544
2025-07-01 03:02:17.557 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:17.565 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:17.574
2025-07-01 03:02:17.581 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:17.593 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:17.601 alo = 213, ahi = 1101
2025-07-01 03:02:17.610 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:17.618 blo = 213, bhi = 1101
2025-07-01 03:02:17.630
2025-07-01 03:02:17.644 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:17.665 g = []
2025-07-01 03:02:17.675 if alo < ahi:
2025-07-01 03:02:17.686 if blo < bhi:
2025-07-01 03:02:17.702 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:17.710 else:
2025-07-01 03:02:17.725 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:17.738 elif blo < bhi:
2025-07-01 03:02:17.746 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:17.758
2025-07-01 03:02:17.766 >       yield from g
2025-07-01 03:02:17.778
2025-07-01 03:02:17.790 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:17.798 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:17.809
2025-07-01 03:02:17.817 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:17.830 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:17.842 alo = 213, ahi = 1101
2025-07-01 03:02:17.854 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:17.862 blo = 213, bhi = 1101
2025-07-01 03:02:17.874
2025-07-01 03:02:17.886 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:17.898 r"""
2025-07-01 03:02:17.906 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:17.918 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:17.935 synch point, and intraline difference marking is done on the
2025-07-01 03:02:17.942 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:17.959
2025-07-01 03:02:17.974 Example:
2025-07-01 03:02:17.986
2025-07-01 03:02:17.998 >>> d = Differ()
2025-07-01 03:02:18.014 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:18.026 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:18.034 >>> print(''.join(results), end="")
2025-07-01 03:02:18.050 - abcDefghiJkl
2025-07-01 03:02:18.066 + abcdefGhijkl
2025-07-01 03:02:18.093 """
2025-07-01 03:02:18.102
2025-07-01 03:02:18.117 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:18.130 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:18.146 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:18.158 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:18.178 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:18.189
2025-07-01 03:02:18.202 # search for the pair that matches best without being identical
2025-07-01 03:02:18.210 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:18.222 # on junk -- unless we have to)
2025-07-01 03:02:18.238 for j in range(blo, bhi):
2025-07-01 03:02:18.246 bj = b[j]
2025-07-01 03:02:18.254 cruncher.set_seq2(bj)
2025-07-01 03:02:18.266 for i in range(alo, ahi):
2025-07-01 03:02:18.274 ai = a[i]
2025-07-01 03:02:18.278 if ai == bj:
2025-07-01 03:02:18.282 if eqi is None:
2025-07-01 03:02:18.287 eqi, eqj = i, j
2025-07-01 03:02:18.291 continue
2025-07-01 03:02:18.296 cruncher.set_seq1(ai)
2025-07-01 03:02:18.300 # computing similarity is expensive, so use the quick
2025-07-01 03:02:18.305 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:18.309 # compares by a factor of 3.
2025-07-01 03:02:18.314 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:18.318 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:18.323 # of the computation is cached by cruncher
2025-07-01 03:02:18.327 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:18.331 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:18.336 cruncher.ratio() > best_ratio:
2025-07-01 03:02:18.341 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:18.345 if best_ratio < cutoff:
2025-07-01 03:02:18.350 # no non-identical "pretty close" pair
2025-07-01 03:02:18.354 if eqi is None:
2025-07-01 03:02:18.359 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:18.363 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:18.368 return
2025-07-01 03:02:18.372 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:18.378 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:18.383 else:
2025-07-01 03:02:18.389 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:18.394 eqi = None
2025-07-01 03:02:18.406
2025-07-01 03:02:18.410 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:18.416 # identical
2025-07-01 03:02:18.420
2025-07-01 03:02:18.425 # pump out diffs from before the synch point
2025-07-01 03:02:18.430 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:18.435
2025-07-01 03:02:18.441 # do intraline marking on the synch pair
2025-07-01 03:02:18.445 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:18.450 if eqi is None:
2025-07-01 03:02:18.454 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:18.459 atags = btags = ""
2025-07-01 03:02:18.463 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:18.470 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:18.478 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:18.486 if tag == 'replace':
2025-07-01 03:02:18.498 atags += '^' * la
2025-07-01 03:02:18.512 btags += '^' * lb
2025-07-01 03:02:18.518 elif tag == 'delete':
2025-07-01 03:02:18.532 atags += '-' * la
2025-07-01 03:02:18.544 elif tag == 'insert':
2025-07-01 03:02:18.554 btags += '+' * lb
2025-07-01 03:02:18.562 elif tag == 'equal':
2025-07-01 03:02:18.577 atags += ' ' * la
2025-07-01 03:02:18.586 btags += ' ' * lb
2025-07-01 03:02:18.598 else:
2025-07-01 03:02:18.618 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:18.626 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:18.635 else:
2025-07-01 03:02:18.648 # the synch pair is identical
2025-07-01 03:02:18.666 yield '  ' + aelt
2025-07-01 03:02:18.673
2025-07-01 03:02:18.686 # pump out diffs from after the synch point
2025-07-01 03:02:18.694 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:18.710
2025-07-01 03:02:18.718 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:18.730 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:18.738
2025-07-01 03:02:18.750 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:18.762 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:18.772 alo = 214, ahi = 1101
2025-07-01 03:02:18.786 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:18.797 blo = 214, bhi = 1101
2025-07-01 03:02:18.810
2025-07-01 03:02:18.822 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:18.829 g = []
2025-07-01 03:02:18.842 if alo < ahi:
2025-07-01 03:02:18.850 if blo < bhi:
2025-07-01 03:02:18.862 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:18.868 else:
2025-07-01 03:02:18.873 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:18.877 elif blo < bhi:
2025-07-01 03:02:18.881 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:18.886
2025-07-01 03:02:18.891 >       yield from g
2025-07-01 03:02:18.895
2025-07-01 03:02:18.899 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:18.904 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:18.908
2025-07-01 03:02:18.913 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:18.918 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:18.923 alo = 214, ahi = 1101
2025-07-01 03:02:18.928 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:18.933 blo = 214, bhi = 1101
2025-07-01 03:02:18.937
2025-07-01 03:02:18.941 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:18.946 r"""
2025-07-01 03:02:18.950 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:18.955 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:18.959 synch point, and intraline difference marking is done on the
2025-07-01 03:02:18.964 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:18.969
2025-07-01 03:02:18.973 Example:
2025-07-01 03:02:18.977
2025-07-01 03:02:18.982 >>> d = Differ()
2025-07-01 03:02:18.987 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:18.991 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:18.996 >>> print(''.join(results), end="")
2025-07-01 03:02:19.000 - abcDefghiJkl
2025-07-01 03:02:19.010 + abcdefGhijkl
2025-07-01 03:02:19.018 """
2025-07-01 03:02:19.023
2025-07-01 03:02:19.027 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:19.032 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:19.036 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:19.041 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:19.045 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:19.049
2025-07-01 03:02:19.054 # search for the pair that matches best without being identical
2025-07-01 03:02:19.059 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:19.063 # on junk -- unless we have to)
2025-07-01 03:02:19.068 for j in range(blo, bhi):
2025-07-01 03:02:19.072 bj = b[j]
2025-07-01 03:02:19.078 cruncher.set_seq2(bj)
2025-07-01 03:02:19.082 for i in range(alo, ahi):
2025-07-01 03:02:19.086 ai = a[i]
2025-07-01 03:02:19.091 if ai == bj:
2025-07-01 03:02:19.095 if eqi is None:
2025-07-01 03:02:19.100 eqi, eqj = i, j
2025-07-01 03:02:19.104 continue
2025-07-01 03:02:19.109 cruncher.set_seq1(ai)
2025-07-01 03:02:19.113 # computing similarity is expensive, so use the quick
2025-07-01 03:02:19.117 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:19.122 # compares by a factor of 3.
2025-07-01 03:02:19.126 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:19.131 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:19.136 # of the computation is cached by cruncher
2025-07-01 03:02:19.141 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:19.145 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:19.150 cruncher.ratio() > best_ratio:
2025-07-01 03:02:19.155 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:19.159 if best_ratio < cutoff:
2025-07-01 03:02:19.164 # no non-identical "pretty close" pair
2025-07-01 03:02:19.169 if eqi is None:
2025-07-01 03:02:19.173 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:19.178 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:19.182 return
2025-07-01 03:02:19.187 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:19.192 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:19.200 else:
2025-07-01 03:02:19.205 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:19.210 eqi = None
2025-07-01 03:02:19.215
2025-07-01 03:02:19.220 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:19.224 # identical
2025-07-01 03:02:19.231
2025-07-01 03:02:19.236 # pump out diffs from before the synch point
2025-07-01 03:02:19.241 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:19.248
2025-07-01 03:02:19.254 # do intraline marking on the synch pair
2025-07-01 03:02:19.261 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:19.265 if eqi is None:
2025-07-01 03:02:19.271 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:19.275 atags = btags = ""
2025-07-01 03:02:19.286 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:19.294 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:19.302 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:19.316 if tag == 'replace':
2025-07-01 03:02:19.331 atags += '^' * la
2025-07-01 03:02:19.348 btags += '^' * lb
2025-07-01 03:02:19.358 elif tag == 'delete':
2025-07-01 03:02:19.377 atags += '-' * la
2025-07-01 03:02:19.392 elif tag == 'insert':
2025-07-01 03:02:19.398 btags += '+' * lb
2025-07-01 03:02:19.410 elif tag == 'equal':
2025-07-01 03:02:19.418 atags += ' ' * la
2025-07-01 03:02:19.434 btags += ' ' * lb
2025-07-01 03:02:19.446 else:
2025-07-01 03:02:19.458 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:19.470 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:19.478 else:
2025-07-01 03:02:19.488 # the synch pair is identical
2025-07-01 03:02:19.493 yield '  ' + aelt
2025-07-01 03:02:19.498
2025-07-01 03:02:19.502 # pump out diffs from after the synch point
2025-07-01 03:02:19.507 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:19.512
2025-07-01 03:02:19.516 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:19.521 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:19.525
2025-07-01 03:02:19.530 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:19.535 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:19.540 alo = 215, ahi = 1101
2025-07-01 03:02:19.545 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:19.549 blo = 215, bhi = 1101
2025-07-01 03:02:19.554
2025-07-01 03:02:19.558 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:19.563 g = []
2025-07-01 03:02:19.568 if alo < ahi:
2025-07-01 03:02:19.572 if blo < bhi:
2025-07-01 03:02:19.577 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:19.581 else:
2025-07-01 03:02:19.585 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:19.590 elif blo < bhi:
2025-07-01 03:02:19.594 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:19.598
2025-07-01 03:02:19.603 >       yield from g
2025-07-01 03:02:19.607
2025-07-01 03:02:19.612 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:19.617 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:19.621
2025-07-01 03:02:19.625 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:19.631 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:19.635 alo = 215, ahi = 1101
2025-07-01 03:02:19.641 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:19.646 blo = 215, bhi = 1101
2025-07-01 03:02:19.650
2025-07-01 03:02:19.654 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:19.659 r"""
2025-07-01 03:02:19.663 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:19.668 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:19.673 synch point, and intraline difference marking is done on the
2025-07-01 03:02:19.678 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:19.682
2025-07-01 03:02:19.687 Example:
2025-07-01 03:02:19.691
2025-07-01 03:02:19.696 >>> d = Differ()
2025-07-01 03:02:19.700 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:19.705 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:19.710 >>> print(''.join(results), end="")
2025-07-01 03:02:19.716 - abcDefghiJkl
2025-07-01 03:02:19.725 + abcdefGhijkl
2025-07-01 03:02:19.738 """
2025-07-01 03:02:19.743
2025-07-01 03:02:19.748 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:19.753 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:19.757 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:19.762 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:19.766 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:19.770
2025-07-01 03:02:19.775 # search for the pair that matches best without being identical
2025-07-01 03:02:19.779 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:19.784 # on junk -- unless we have to)
2025-07-01 03:02:19.788 for j in range(blo, bhi):
2025-07-01 03:02:19.793 bj = b[j]
2025-07-01 03:02:19.797 cruncher.set_seq2(bj)
2025-07-01 03:02:19.801 for i in range(alo, ahi):
2025-07-01 03:02:19.806 ai = a[i]
2025-07-01 03:02:19.810 if ai == bj:
2025-07-01 03:02:19.815 if eqi is None:
2025-07-01 03:02:19.819 eqi, eqj = i, j
2025-07-01 03:02:19.824 continue
2025-07-01 03:02:19.828 cruncher.set_seq1(ai)
2025-07-01 03:02:19.834 # computing similarity is expensive, so use the quick
2025-07-01 03:02:19.838 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:19.843 # compares by a factor of 3.
2025-07-01 03:02:19.847 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:19.852 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:19.856 # of the computation is cached by cruncher
2025-07-01 03:02:19.861 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:19.865 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:19.870 cruncher.ratio() > best_ratio:
2025-07-01 03:02:19.874 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:19.879 if best_ratio < cutoff:
2025-07-01 03:02:19.884 # no non-identical "pretty close" pair
2025-07-01 03:02:19.888 if eqi is None:
2025-07-01 03:02:19.893 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:19.897 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:19.902 return
2025-07-01 03:02:19.909 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:19.915 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:19.919 else:
2025-07-01 03:02:19.923 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:19.928 eqi = None
2025-07-01 03:02:19.932
2025-07-01 03:02:19.940 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:19.944 # identical
2025-07-01 03:02:19.949
2025-07-01 03:02:19.953 # pump out diffs from before the synch point
2025-07-01 03:02:19.957 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:19.965
2025-07-01 03:02:19.973 # do intraline marking on the synch pair
2025-07-01 03:02:19.982 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:19.990 if eqi is None:
2025-07-01 03:02:20.001 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:20.015 atags = btags = ""
2025-07-01 03:02:20.034 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:20.043 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:20.054 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:20.069 if tag == 'replace':
2025-07-01 03:02:20.085 atags += '^' * la
2025-07-01 03:02:20.098 btags += '^' * lb
2025-07-01 03:02:20.116 elif tag == 'delete':
2025-07-01 03:02:20.129 atags += '-' * la
2025-07-01 03:02:20.141 elif tag == 'insert':
2025-07-01 03:02:20.154 btags += '+' * lb
2025-07-01 03:02:20.162 elif tag == 'equal':
2025-07-01 03:02:20.178 atags += ' ' * la
2025-07-01 03:02:20.190 btags += ' ' * lb
2025-07-01 03:02:20.198 else:
2025-07-01 03:02:20.214 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:20.226 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:20.242 else:
2025-07-01 03:02:20.258 # the synch pair is identical
2025-07-01 03:02:20.266 yield '  ' + aelt
2025-07-01 03:02:20.275
2025-07-01 03:02:20.293 # pump out diffs from after the synch point
2025-07-01 03:02:20.310 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:20.326
2025-07-01 03:02:20.334 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:20.350 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:20.366
2025-07-01 03:02:20.378 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:20.394 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:20.402 alo = 216, ahi = 1101
2025-07-01 03:02:20.414 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:20.426 blo = 216, bhi = 1101
2025-07-01 03:02:20.442
2025-07-01 03:02:20.458 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:20.474 g = []
2025-07-01 03:02:20.486 if alo < ahi:
2025-07-01 03:02:20.491 if blo < bhi:
2025-07-01 03:02:20.502 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:20.514 else:
2025-07-01 03:02:20.526 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:20.538 elif blo < bhi:
2025-07-01 03:02:20.546 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:20.562
2025-07-01 03:02:20.570 >       yield from g
2025-07-01 03:02:20.581
2025-07-01 03:02:20.594 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:20.606 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:20.618
2025-07-01 03:02:20.630 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:20.638 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:20.654 alo = 216, ahi = 1101
2025-07-01 03:02:20.670 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:20.678 blo = 216, bhi = 1101
2025-07-01 03:02:20.690
2025-07-01 03:02:20.698 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:20.710 r"""
2025-07-01 03:02:20.726 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:20.737 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:20.754 synch point, and intraline difference marking is done on the
2025-07-01 03:02:20.762 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:20.771
2025-07-01 03:02:20.790 Example:
2025-07-01 03:02:20.805
2025-07-01 03:02:20.818 >>> d = Differ()
2025-07-01 03:02:20.831 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:20.841 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:20.848 >>> print(''.join(results), end="")
2025-07-01 03:02:20.868 - abcDefghiJkl
2025-07-01 03:02:20.905 + abcdefGhijkl
2025-07-01 03:02:20.937 """
2025-07-01 03:02:20.949
2025-07-01 03:02:20.961 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:20.974 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:20.988 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:21.007 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:21.018 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:21.030
2025-07-01 03:02:21.042 # search for the pair that matches best without being identical
2025-07-01 03:02:21.050 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:21.058 # on junk -- unless we have to)
2025-07-01 03:02:21.070 for j in range(blo, bhi):
2025-07-01 03:02:21.078 bj = b[j]
2025-07-01 03:02:21.090 cruncher.set_seq2(bj)
2025-07-01 03:02:21.102 for i in range(alo, ahi):
2025-07-01 03:02:21.110 ai = a[i]
2025-07-01 03:02:21.122 if ai == bj:
2025-07-01 03:02:21.130 if eqi is None:
2025-07-01 03:02:21.141 eqi, eqj = i, j
2025-07-01 03:02:21.154 continue
2025-07-01 03:02:21.162 cruncher.set_seq1(ai)
2025-07-01 03:02:21.174 # computing similarity is expensive, so use the quick
2025-07-01 03:02:21.186 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:21.198 # compares by a factor of 3.
2025-07-01 03:02:21.214 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:21.222 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:21.234 # of the computation is cached by cruncher
2025-07-01 03:02:21.242 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:21.258 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:21.266 cruncher.ratio() > best_ratio:
2025-07-01 03:02:21.278 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:21.286 if best_ratio < cutoff:
2025-07-01 03:02:21.298 # no non-identical "pretty close" pair
2025-07-01 03:02:21.310 if eqi is None:
2025-07-01 03:02:21.322 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:21.330 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:21.338 return
2025-07-01 03:02:21.350 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:21.358 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:21.370 else:
2025-07-01 03:02:21.380 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:21.398 eqi = None
2025-07-01 03:02:21.406
2025-07-01 03:02:21.414 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:21.421 # identical
2025-07-01 03:02:21.433
2025-07-01 03:02:21.443 # pump out diffs from before the synch point
2025-07-01 03:02:21.458 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:21.466
2025-07-01 03:02:21.476 # do intraline marking on the synch pair
2025-07-01 03:02:21.485 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:21.494 if eqi is None:
2025-07-01 03:02:21.502 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:21.514 atags = btags = ""
2025-07-01 03:02:21.526 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:21.538 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:21.548 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:21.566 if tag == 'replace':
2025-07-01 03:02:21.580 atags += '^' * la
2025-07-01 03:02:21.590 btags += '^' * lb
2025-07-01 03:02:21.600 elif tag == 'delete':
2025-07-01 03:02:21.617 atags += '-' * la
2025-07-01 03:02:21.630 elif tag == 'insert':
2025-07-01 03:02:21.637 btags += '+' * lb
2025-07-01 03:02:21.650 elif tag == 'equal':
2025-07-01 03:02:21.658 atags += ' ' * la
2025-07-01 03:02:21.670 btags += ' ' * lb
2025-07-01 03:02:21.682 else:
2025-07-01 03:02:21.696 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:21.712 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:21.728 else:
2025-07-01 03:02:21.742 # the synch pair is identical
2025-07-01 03:02:21.750 yield '  ' + aelt
2025-07-01 03:02:21.758
2025-07-01 03:02:21.774 # pump out diffs from after the synch point
2025-07-01 03:02:21.786 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:21.794
2025-07-01 03:02:21.802 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:21.814 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:21.821
2025-07-01 03:02:21.838 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:21.846 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:21.862 alo = 217, ahi = 1101
2025-07-01 03:02:21.870 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:21.882 blo = 217, bhi = 1101
2025-07-01 03:02:21.890
2025-07-01 03:02:21.906 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:21.914 g = []
2025-07-01 03:02:21.928 if alo < ahi:
2025-07-01 03:02:21.942 if blo < bhi:
2025-07-01 03:02:21.949 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:21.962 else:
2025-07-01 03:02:21.974 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:21.982 elif blo < bhi:
2025-07-01 03:02:21.990 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:22.002
2025-07-01 03:02:22.014 >       yield from g
2025-07-01 03:02:22.026
2025-07-01 03:02:22.034 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:22.046 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:22.054
2025-07-01 03:02:22.066 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:22.077 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:22.089 alo = 217, ahi = 1101
2025-07-01 03:02:22.106 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:22.117 blo = 217, bhi = 1101
2025-07-01 03:02:22.134
2025-07-01 03:02:22.145 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:22.162 r"""
2025-07-01 03:02:22.177 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:22.194 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:22.206 synch point, and intraline difference marking is done on the
2025-07-01 03:02:22.216 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:22.224
2025-07-01 03:02:22.239 Example:
2025-07-01 03:02:22.254
2025-07-01 03:02:22.261 >>> d = Differ()
2025-07-01 03:02:22.270 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:22.287 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:22.295 >>> print(''.join(results), end="")
2025-07-01 03:02:22.305 - abcDefghiJkl
2025-07-01 03:02:22.337 + abcdefGhijkl
2025-07-01 03:02:22.362 """
2025-07-01 03:02:22.381
2025-07-01 03:02:22.397 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:22.409 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:22.425 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:22.438 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:22.447 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:22.458
2025-07-01 03:02:22.474 # search for the pair that matches best without being identical
2025-07-01 03:02:22.482 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:22.497 # on junk -- unless we have to)
2025-07-01 03:02:22.510 for j in range(blo, bhi):
2025-07-01 03:02:22.522 bj = b[j]
2025-07-01 03:02:22.530 cruncher.set_seq2(bj)
2025-07-01 03:02:22.546 for i in range(alo, ahi):
2025-07-01 03:02:22.558 ai = a[i]
2025-07-01 03:02:22.566 if ai == bj:
2025-07-01 03:02:22.580 if eqi is None:
2025-07-01 03:02:22.590 eqi, eqj = i, j
2025-07-01 03:02:22.606 continue
2025-07-01 03:02:22.618 cruncher.set_seq1(ai)
2025-07-01 03:02:22.626 # computing similarity is expensive, so use the quick
2025-07-01 03:02:22.633 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:22.646 # compares by a factor of 3.
2025-07-01 03:02:22.658 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:22.670 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:22.678 # of the computation is cached by cruncher
2025-07-01 03:02:22.690 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:22.698 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:22.710 cruncher.ratio() > best_ratio:
2025-07-01 03:02:22.718 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:22.730 if best_ratio < cutoff:
2025-07-01 03:02:22.742 # no non-identical "pretty close" pair
2025-07-01 03:02:22.756 if eqi is None:
2025-07-01 03:02:22.766 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:22.782 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:22.793 return
2025-07-01 03:02:22.806 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:22.814 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:22.822 else:
2025-07-01 03:02:22.834 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:22.842 eqi = None
2025-07-01 03:02:22.854
2025-07-01 03:02:22.863 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:22.878 # identical
2025-07-01 03:02:22.886
2025-07-01 03:02:22.893 # pump out diffs from before the synch point
2025-07-01 03:02:22.904 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:22.922
2025-07-01 03:02:22.934 # do intraline marking on the synch pair
2025-07-01 03:02:22.942 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:22.956 if eqi is None:
2025-07-01 03:02:22.966 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:22.982 atags = btags = ""
2025-07-01 03:02:22.990 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:23.003 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:23.018 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:23.026 if tag == 'replace':
2025-07-01 03:02:23.038 atags += '^' * la
2025-07-01 03:02:23.050 btags += '^' * lb
2025-07-01 03:02:23.059 elif tag == 'delete':
2025-07-01 03:02:23.066 atags += '-' * la
2025-07-01 03:02:23.078 elif tag == 'insert':
2025-07-01 03:02:23.086 btags += '+' * lb
2025-07-01 03:02:23.094 elif tag == 'equal':
2025-07-01 03:02:23.108 atags += ' ' * la
2025-07-01 03:02:23.120 btags += ' ' * lb
2025-07-01 03:02:23.130 else:
2025-07-01 03:02:23.142 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:23.153 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:23.162 else:
2025-07-01 03:02:23.175 # the synch pair is identical
2025-07-01 03:02:23.190 yield '  ' + aelt
2025-07-01 03:02:23.203
2025-07-01 03:02:23.218 # pump out diffs from after the synch point
2025-07-01 03:02:23.226 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:23.238
2025-07-01 03:02:23.250 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:23.258 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:23.274
2025-07-01 03:02:23.282 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:23.298 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:23.305 alo = 218, ahi = 1101
2025-07-01 03:02:23.322 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:23.329 blo = 218, bhi = 1101
2025-07-01 03:02:23.350
2025-07-01 03:02:23.358 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:23.366 g = []
2025-07-01 03:02:23.376 if alo < ahi:
2025-07-01 03:02:23.390 if blo < bhi:
2025-07-01 03:02:23.397 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:23.409 else:
2025-07-01 03:02:23.417 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:23.430 elif blo < bhi:
2025-07-01 03:02:23.438 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:23.453
2025-07-01 03:02:23.464 >       yield from g
2025-07-01 03:02:23.478
2025-07-01 03:02:23.490 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:23.498 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:23.510
2025-07-01 03:02:23.515 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:23.534 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:23.545 alo = 218, ahi = 1101
2025-07-01 03:02:23.557 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:23.570 blo = 218, bhi = 1101
2025-07-01 03:02:23.590
2025-07-01 03:02:23.597 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:23.605 r"""
2025-07-01 03:02:23.619 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:23.631 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:23.642 synch point, and intraline difference marking is done on the
2025-07-01 03:02:23.646 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:23.652
2025-07-01 03:02:23.662 Example:
2025-07-01 03:02:23.678
2025-07-01 03:02:23.685 >>> d = Differ()
2025-07-01 03:02:23.698 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:23.709 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:23.722 >>> print(''.join(results), end="")
2025-07-01 03:02:23.732 - abcDefghiJkl
2025-07-01 03:02:23.751 + abcdefGhijkl
2025-07-01 03:02:23.773 """
2025-07-01 03:02:23.786
2025-07-01 03:02:23.801 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:23.816 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:23.833 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:23.845 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:23.859 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:23.870
2025-07-01 03:02:23.890 # search for the pair that matches best without being identical
2025-07-01 03:02:23.898 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:23.914 # on junk -- unless we have to)
2025-07-01 03:02:23.921 for j in range(blo, bhi):
2025-07-01 03:02:23.934 bj = b[j]
2025-07-01 03:02:23.942 cruncher.set_seq2(bj)
2025-07-01 03:02:23.949 for i in range(alo, ahi):
2025-07-01 03:02:23.966 ai = a[i]
2025-07-01 03:02:23.973 if ai == bj:
2025-07-01 03:02:23.982 if eqi is None:
2025-07-01 03:02:23.998 eqi, eqj = i, j
2025-07-01 03:02:24.010 continue
2025-07-01 03:02:24.022 cruncher.set_seq1(ai)
2025-07-01 03:02:24.030 # computing similarity is expensive, so use the quick
2025-07-01 03:02:24.046 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:24.054 # compares by a factor of 3.
2025-07-01 03:02:24.069 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:24.082 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:24.089 # of the computation is cached by cruncher
2025-07-01 03:02:24.102 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:24.114 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:24.121 cruncher.ratio() > best_ratio:
2025-07-01 03:02:24.138 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:24.146 if best_ratio < cutoff:
2025-07-01 03:02:24.156 # no non-identical "pretty close" pair
2025-07-01 03:02:24.170 if eqi is None:
2025-07-01 03:02:24.192 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:24.202 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:24.210 return
2025-07-01 03:02:24.222 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:24.230 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:24.242 else:
2025-07-01 03:02:24.250 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:24.262 eqi = None
2025-07-01 03:02:24.270
2025-07-01 03:02:24.282 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:24.288 # identical
2025-07-01 03:02:24.292
2025-07-01 03:02:24.297 # pump out diffs from before the synch point
2025-07-01 03:02:24.302 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:24.306
2025-07-01 03:02:24.311 # do intraline marking on the synch pair
2025-07-01 03:02:24.315 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:24.320 if eqi is None:
2025-07-01 03:02:24.325 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:24.329 atags = btags = ""
2025-07-01 03:02:24.333 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:24.338 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:24.342 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:24.347 if tag == 'replace':
2025-07-01 03:02:24.351 atags += '^' * la
2025-07-01 03:02:24.356 btags += '^' * lb
2025-07-01 03:02:24.360 elif tag == 'delete':
2025-07-01 03:02:24.365 atags += '-' * la
2025-07-01 03:02:24.369 elif tag == 'insert':
2025-07-01 03:02:24.374 btags += '+' * lb
2025-07-01 03:02:24.379 elif tag == 'equal':
2025-07-01 03:02:24.383 atags += ' ' * la
2025-07-01 03:02:24.388 btags += ' ' * lb
2025-07-01 03:02:24.393 else:
2025-07-01 03:02:24.398 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:24.402 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:24.407 else:
2025-07-01 03:02:24.412 # the synch pair is identical
2025-07-01 03:02:24.416 yield '  ' + aelt
2025-07-01 03:02:24.421
2025-07-01 03:02:24.425 # pump out diffs from after the synch point
2025-07-01 03:02:24.430 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:24.435
2025-07-01 03:02:24.439 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:24.444 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:24.449
2025-07-01 03:02:24.453 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:24.459 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:24.463 alo = 219, ahi = 1101
2025-07-01 03:02:24.469 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:24.475 blo = 219, bhi = 1101
2025-07-01 03:02:24.479
2025-07-01 03:02:24.484 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:24.488 g = []
2025-07-01 03:02:24.493 if alo < ahi:
2025-07-01 03:02:24.498 if blo < bhi:
2025-07-01 03:02:24.503 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:24.507 else:
2025-07-01 03:02:24.512 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:24.516 elif blo < bhi:
2025-07-01 03:02:24.521 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:24.525
2025-07-01 03:02:24.530 >       yield from g
2025-07-01 03:02:24.534
2025-07-01 03:02:24.538 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:24.543 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:24.547
2025-07-01 03:02:24.552 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:24.557 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:24.561 alo = 219, ahi = 1101
2025-07-01 03:02:24.566 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:24.571 blo = 219, bhi = 1101
2025-07-01 03:02:24.577
2025-07-01 03:02:24.582 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:24.587 r"""
2025-07-01 03:02:24.591 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:24.596 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:24.601 synch point, and intraline difference marking is done on the
2025-07-01 03:02:24.605 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:24.609
2025-07-01 03:02:24.614 Example:
2025-07-01 03:02:24.618
2025-07-01 03:02:24.622 >>> d = Differ()
2025-07-01 03:02:24.627 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:24.631 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:24.636 >>> print(''.join(results), end="")
2025-07-01 03:02:24.640 - abcDefghiJkl
2025-07-01 03:02:24.649 + abcdefGhijkl
2025-07-01 03:02:24.658 """
2025-07-01 03:02:24.662
2025-07-01 03:02:24.666 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:24.671 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:24.679 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:24.684 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:24.688 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:24.693
2025-07-01 03:02:24.697 # search for the pair that matches best without being identical
2025-07-01 03:02:24.702 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:24.706 # on junk -- unless we have to)
2025-07-01 03:02:24.711 for j in range(blo, bhi):
2025-07-01 03:02:24.715 bj = b[j]
2025-07-01 03:02:24.719 cruncher.set_seq2(bj)
2025-07-01 03:02:24.724 for i in range(alo, ahi):
2025-07-01 03:02:24.728 ai = a[i]
2025-07-01 03:02:24.733 if ai == bj:
2025-07-01 03:02:24.737 if eqi is None:
2025-07-01 03:02:24.741 eqi, eqj = i, j
2025-07-01 03:02:24.746 continue
2025-07-01 03:02:24.750 cruncher.set_seq1(ai)
2025-07-01 03:02:24.755 # computing similarity is expensive, so use the quick
2025-07-01 03:02:24.759 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:24.764 # compares by a factor of 3.
2025-07-01 03:02:24.768 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:24.778 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:24.794 # of the computation is cached by cruncher
2025-07-01 03:02:24.805 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:24.821 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:24.834 cruncher.ratio() > best_ratio:
2025-07-01 03:02:24.843 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:24.857 if best_ratio < cutoff:
2025-07-01 03:02:24.869 # no non-identical "pretty close" pair
2025-07-01 03:02:24.882 if eqi is None:
2025-07-01 03:02:24.894 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:24.903 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:24.918 return
2025-07-01 03:02:24.926 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:24.938 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:24.946 else:
2025-07-01 03:02:24.953 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:24.970 eqi = None
2025-07-01 03:02:24.977
2025-07-01 03:02:24.988 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:24.998 # identical
2025-07-01 03:02:25.006
2025-07-01 03:02:25.022 # pump out diffs from before the synch point
2025-07-01 03:02:25.031 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:25.036
2025-07-01 03:02:25.040 # do intraline marking on the synch pair
2025-07-01 03:02:25.045 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:25.054 if eqi is None:
2025-07-01 03:02:25.059 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:25.063 atags = btags = ""
2025-07-01 03:02:25.068 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:25.072 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:25.076 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:25.081 if tag == 'replace':
2025-07-01 03:02:25.090 atags += '^' * la
2025-07-01 03:02:25.098 btags += '^' * lb
2025-07-01 03:02:25.106 elif tag == 'delete':
2025-07-01 03:02:25.114 atags += '-' * la
2025-07-01 03:02:25.121 elif tag == 'insert':
2025-07-01 03:02:25.126 btags += '+' * lb
2025-07-01 03:02:25.130 elif tag == 'equal':
2025-07-01 03:02:25.134 atags += ' ' * la
2025-07-01 03:02:25.139 btags += ' ' * lb
2025-07-01 03:02:25.143 else:
2025-07-01 03:02:25.148 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:25.152 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:25.156 else:
2025-07-01 03:02:25.165 # the synch pair is identical
2025-07-01 03:02:25.174 yield '  ' + aelt
2025-07-01 03:02:25.181
2025-07-01 03:02:25.198 # pump out diffs from after the synch point
2025-07-01 03:02:25.209 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:25.226
2025-07-01 03:02:25.237 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:25.251 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:25.263
2025-07-01 03:02:25.277 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:25.291 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:25.301 alo = 220, ahi = 1101
2025-07-01 03:02:25.314 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:25.329 blo = 220, bhi = 1101
2025-07-01 03:02:25.342
2025-07-01 03:02:25.354 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:25.366 g = []
2025-07-01 03:02:25.374 if alo < ahi:
2025-07-01 03:02:25.386 if blo < bhi:
2025-07-01 03:02:25.398 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:25.413 else:
2025-07-01 03:02:25.422 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:25.439 elif blo < bhi:
2025-07-01 03:02:25.457 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:25.470
2025-07-01 03:02:25.480 >       yield from g
2025-07-01 03:02:25.494
2025-07-01 03:02:25.501 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:25.514 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:25.522
2025-07-01 03:02:25.534 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:25.553 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:25.566 alo = 220, ahi = 1101
2025-07-01 03:02:25.582 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:25.598 blo = 220, bhi = 1101
2025-07-01 03:02:25.606
2025-07-01 03:02:25.618 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:25.630 r"""
2025-07-01 03:02:25.646 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:25.658 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:25.670 synch point, and intraline difference marking is done on the
2025-07-01 03:02:25.680 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:25.695
2025-07-01 03:02:25.706 Example:
2025-07-01 03:02:25.718
2025-07-01 03:02:25.726 >>> d = Differ()
2025-07-01 03:02:25.738 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:25.746 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:25.751 >>> print(''.join(results), end="")
2025-07-01 03:02:25.763 - abcDefghiJkl
2025-07-01 03:02:25.790 + abcdefGhijkl
2025-07-01 03:02:25.810 """
2025-07-01 03:02:25.822
2025-07-01 03:02:25.830 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:25.842 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:25.856 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:25.874 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:25.882 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:25.894
2025-07-01 03:02:25.910 # search for the pair that matches best without being identical
2025-07-01 03:02:25.918 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:25.930 # on junk -- unless we have to)
2025-07-01 03:02:25.938 for j in range(blo, bhi):
2025-07-01 03:02:25.949 bj = b[j]
2025-07-01 03:02:25.966 cruncher.set_seq2(bj)
2025-07-01 03:02:25.978 for i in range(alo, ahi):
2025-07-01 03:02:25.997 ai = a[i]
2025-07-01 03:02:26.013 if ai == bj:
2025-07-01 03:02:26.025 if eqi is None:
2025-07-01 03:02:26.031 eqi, eqj = i, j
2025-07-01 03:02:26.038 continue
2025-07-01 03:02:26.044 cruncher.set_seq1(ai)
2025-07-01 03:02:26.050 # computing similarity is expensive, so use the quick
2025-07-01 03:02:26.057 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:26.066 # compares by a factor of 3.
2025-07-01 03:02:26.073 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:26.080 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:26.086 # of the computation is cached by cruncher
2025-07-01 03:02:26.092 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:26.099 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:26.105 cruncher.ratio() > best_ratio:
2025-07-01 03:02:26.111 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:26.117 if best_ratio < cutoff:
2025-07-01 03:02:26.126 # no non-identical "pretty close" pair
2025-07-01 03:02:26.132 if eqi is None:
2025-07-01 03:02:26.139 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:26.145 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:26.151 return
2025-07-01 03:02:26.155 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:26.160 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:26.164 else:
2025-07-01 03:02:26.169 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:26.174 eqi = None
2025-07-01 03:02:26.178
2025-07-01 03:02:26.183 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:26.189 # identical
2025-07-01 03:02:26.195
2025-07-01 03:02:26.199 # pump out diffs from before the synch point
2025-07-01 03:02:26.204 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:26.208
2025-07-01 03:02:26.213 # do intraline marking on the synch pair
2025-07-01 03:02:26.217 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:26.222 if eqi is None:
2025-07-01 03:02:26.227 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:26.231 atags = btags = ""
2025-07-01 03:02:26.236 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:26.240 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:26.245 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:26.250 if tag == 'replace':
2025-07-01 03:02:26.254 atags += '^' * la
2025-07-01 03:02:26.259 btags += '^' * lb
2025-07-01 03:02:26.263 elif tag == 'delete':
2025-07-01 03:02:26.268 atags += '-' * la
2025-07-01 03:02:26.272 elif tag == 'insert':
2025-07-01 03:02:26.277 btags += '+' * lb
2025-07-01 03:02:26.281 elif tag == 'equal':
2025-07-01 03:02:26.285 atags += ' ' * la
2025-07-01 03:02:26.293 btags += ' ' * lb
2025-07-01 03:02:26.297 else:
2025-07-01 03:02:26.302 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:26.307 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:26.311 else:
2025-07-01 03:02:26.316 # the synch pair is identical
2025-07-01 03:02:26.320 yield '  ' + aelt
2025-07-01 03:02:26.324
2025-07-01 03:02:26.329 # pump out diffs from after the synch point
2025-07-01 03:02:26.333 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:26.338
2025-07-01 03:02:26.342 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:26.347 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:26.351
2025-07-01 03:02:26.356 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:26.361 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:26.365 alo = 221, ahi = 1101
2025-07-01 03:02:26.370 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:26.374 blo = 221, bhi = 1101
2025-07-01 03:02:26.379
2025-07-01 03:02:26.384 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:26.389 g = []
2025-07-01 03:02:26.394 if alo < ahi:
2025-07-01 03:02:26.398 if blo < bhi:
2025-07-01 03:02:26.403 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:26.408 else:
2025-07-01 03:02:26.412 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:26.416 elif blo < bhi:
2025-07-01 03:02:26.421 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:26.425
2025-07-01 03:02:26.429 >       yield from g
2025-07-01 03:02:26.434
2025-07-01 03:02:26.440 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:26.446 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:26.452
2025-07-01 03:02:26.460 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:26.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:26.472 alo = 221, ahi = 1101
2025-07-01 03:02:26.479 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:26.485 blo = 221, bhi = 1101
2025-07-01 03:02:26.491
2025-07-01 03:02:26.497 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:26.503 r"""
2025-07-01 03:02:26.509 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:26.516 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:26.522 synch point, and intraline difference marking is done on the
2025-07-01 03:02:26.528 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:26.534
2025-07-01 03:02:26.540 Example:
2025-07-01 03:02:26.546
2025-07-01 03:02:26.553 >>> d = Differ()
2025-07-01 03:02:26.559 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:26.566 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:26.572 >>> print(''.join(results), end="")
2025-07-01 03:02:26.578 - abcDefghiJkl
2025-07-01 03:02:26.590 + abcdefGhijkl
2025-07-01 03:02:26.603 """
2025-07-01 03:02:26.608
2025-07-01 03:02:26.615 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:26.621 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:26.627 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:26.632 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:26.636 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:26.640
2025-07-01 03:02:26.645 # search for the pair that matches best without being identical
2025-07-01 03:02:26.649 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:26.654 # on junk -- unless we have to)
2025-07-01 03:02:26.659 for j in range(blo, bhi):
2025-07-01 03:02:26.663 bj = b[j]
2025-07-01 03:02:26.668 cruncher.set_seq2(bj)
2025-07-01 03:02:26.673 for i in range(alo, ahi):
2025-07-01 03:02:26.677 ai = a[i]
2025-07-01 03:02:26.681 if ai == bj:
2025-07-01 03:02:26.686 if eqi is None:
2025-07-01 03:02:26.690 eqi, eqj = i, j
2025-07-01 03:02:26.694 continue
2025-07-01 03:02:26.699 cruncher.set_seq1(ai)
2025-07-01 03:02:26.703 # computing similarity is expensive, so use the quick
2025-07-01 03:02:26.708 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:26.712 # compares by a factor of 3.
2025-07-01 03:02:26.716 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:26.721 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:26.725 # of the computation is cached by cruncher
2025-07-01 03:02:26.730 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:26.734 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:26.738 cruncher.ratio() > best_ratio:
2025-07-01 03:02:26.747 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:26.751 if best_ratio < cutoff:
2025-07-01 03:02:26.756 # no non-identical "pretty close" pair
2025-07-01 03:02:26.760 if eqi is None:
2025-07-01 03:02:26.765 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:26.769 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:26.773 return
2025-07-01 03:02:26.778 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:26.783 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:26.788 else:
2025-07-01 03:02:26.793 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:26.797 eqi = None
2025-07-01 03:02:26.802
2025-07-01 03:02:26.806 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:26.811 # identical
2025-07-01 03:02:26.815
2025-07-01 03:02:26.822 # pump out diffs from before the synch point
2025-07-01 03:02:26.826 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:26.831
2025-07-01 03:02:26.838 # do intraline marking on the synch pair
2025-07-01 03:02:26.844 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:26.851 if eqi is None:
2025-07-01 03:02:26.858 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:26.864 atags = btags = ""
2025-07-01 03:02:26.870 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:26.876 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:26.882 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:26.887 if tag == 'replace':
2025-07-01 03:02:26.891 atags += '^' * la
2025-07-01 03:02:26.896 btags += '^' * lb
2025-07-01 03:02:26.900 elif tag == 'delete':
2025-07-01 03:02:26.904 atags += '-' * la
2025-07-01 03:02:26.909 elif tag == 'insert':
2025-07-01 03:02:26.915 btags += '+' * lb
2025-07-01 03:02:26.920 elif tag == 'equal':
2025-07-01 03:02:26.924 atags += ' ' * la
2025-07-01 03:02:26.929 btags += ' ' * lb
2025-07-01 03:02:26.933 else:
2025-07-01 03:02:26.937 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:26.942 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:26.947 else:
2025-07-01 03:02:26.951 # the synch pair is identical
2025-07-01 03:02:26.956 yield '  ' + aelt
2025-07-01 03:02:26.960
2025-07-01 03:02:26.964 # pump out diffs from after the synch point
2025-07-01 03:02:26.969 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:26.973
2025-07-01 03:02:26.978 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:26.982 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:26.988
2025-07-01 03:02:26.993 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:26.998 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:27.002 alo = 224, ahi = 1101
2025-07-01 03:02:27.007 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:27.012 blo = 224, bhi = 1101
2025-07-01 03:02:27.016
2025-07-01 03:02:27.021 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:27.025 g = []
2025-07-01 03:02:27.029 if alo < ahi:
2025-07-01 03:02:27.034 if blo < bhi:
2025-07-01 03:02:27.038 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:27.042 else:
2025-07-01 03:02:27.046 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:27.051 elif blo < bhi:
2025-07-01 03:02:27.055 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:27.059
2025-07-01 03:02:27.063 >       yield from g
2025-07-01 03:02:27.067
2025-07-01 03:02:27.072 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:27.076 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:27.081
2025-07-01 03:02:27.085 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:27.090 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:27.094 alo = 224, ahi = 1101
2025-07-01 03:02:27.099 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:27.103 blo = 224, bhi = 1101
2025-07-01 03:02:27.108
2025-07-01 03:02:27.112 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:27.117 r"""
2025-07-01 03:02:27.122 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:27.127 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:27.131 synch point, and intraline difference marking is done on the
2025-07-01 03:02:27.136 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:27.140
2025-07-01 03:02:27.144 Example:
2025-07-01 03:02:27.149
2025-07-01 03:02:27.153 >>> d = Differ()
2025-07-01 03:02:27.158 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:27.162 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:27.167 >>> print(''.join(results), end="")
2025-07-01 03:02:27.171 - abcDefghiJkl
2025-07-01 03:02:27.180 + abcdefGhijkl
2025-07-01 03:02:27.189 """
2025-07-01 03:02:27.193
2025-07-01 03:02:27.197 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:27.202 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:27.206 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:27.211 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:27.216 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:27.220
2025-07-01 03:02:27.224 # search for the pair that matches best without being identical
2025-07-01 03:02:27.229 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:27.233 # on junk -- unless we have to)
2025-07-01 03:02:27.238 for j in range(blo, bhi):
2025-07-01 03:02:27.242 bj = b[j]
2025-07-01 03:02:27.246 cruncher.set_seq2(bj)
2025-07-01 03:02:27.250 for i in range(alo, ahi):
2025-07-01 03:02:27.255 ai = a[i]
2025-07-01 03:02:27.259 if ai == bj:
2025-07-01 03:02:27.263 if eqi is None:
2025-07-01 03:02:27.268 eqi, eqj = i, j
2025-07-01 03:02:27.272 continue
2025-07-01 03:02:27.276 cruncher.set_seq1(ai)
2025-07-01 03:02:27.281 # computing similarity is expensive, so use the quick
2025-07-01 03:02:27.285 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:27.290 # compares by a factor of 3.
2025-07-01 03:02:27.294 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:27.299 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:27.304 # of the computation is cached by cruncher
2025-07-01 03:02:27.309 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:27.313 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:27.318 cruncher.ratio() > best_ratio:
2025-07-01 03:02:27.322 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:27.327 if best_ratio < cutoff:
2025-07-01 03:02:27.331 # no non-identical "pretty close" pair
2025-07-01 03:02:27.335 if eqi is None:
2025-07-01 03:02:27.340 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:27.344 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:27.349 return
2025-07-01 03:02:27.353 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:27.358 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:27.362 else:
2025-07-01 03:02:27.366 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:27.371 eqi = None
2025-07-01 03:02:27.375
2025-07-01 03:02:27.379 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:27.383 # identical
2025-07-01 03:02:27.388
2025-07-01 03:02:27.392 # pump out diffs from before the synch point
2025-07-01 03:02:27.397 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:27.402
2025-07-01 03:02:27.406 # do intraline marking on the synch pair
2025-07-01 03:02:27.411 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:27.415 if eqi is None:
2025-07-01 03:02:27.420 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:27.424 atags = btags = ""
2025-07-01 03:02:27.428 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:27.433 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:27.437 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:27.442 if tag == 'replace':
2025-07-01 03:02:27.446 atags += '^' * la
2025-07-01 03:02:27.450 btags += '^' * lb
2025-07-01 03:02:27.454 elif tag == 'delete':
2025-07-01 03:02:27.459 atags += '-' * la
2025-07-01 03:02:27.463 elif tag == 'insert':
2025-07-01 03:02:27.467 btags += '+' * lb
2025-07-01 03:02:27.472 elif tag == 'equal':
2025-07-01 03:02:27.476 atags += ' ' * la
2025-07-01 03:02:27.480 btags += ' ' * lb
2025-07-01 03:02:27.485 else:
2025-07-01 03:02:27.489 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:27.493 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:27.497 else:
2025-07-01 03:02:27.502 # the synch pair is identical
2025-07-01 03:02:27.507 yield '  ' + aelt
2025-07-01 03:02:27.512
2025-07-01 03:02:27.516 # pump out diffs from after the synch point
2025-07-01 03:02:27.521 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:27.525
2025-07-01 03:02:27.529 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:27.534 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:27.538
2025-07-01 03:02:27.542 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:27.547 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:27.552 alo = 225, ahi = 1101
2025-07-01 03:02:27.557 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:27.561 blo = 225, bhi = 1101
2025-07-01 03:02:27.566
2025-07-01 03:02:27.570 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:27.575 g = []
2025-07-01 03:02:27.579 if alo < ahi:
2025-07-01 03:02:27.584 if blo < bhi:
2025-07-01 03:02:27.589 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:27.594 else:
2025-07-01 03:02:27.598 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:27.603 elif blo < bhi:
2025-07-01 03:02:27.608 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:27.613
2025-07-01 03:02:27.618 >       yield from g
2025-07-01 03:02:27.622
2025-07-01 03:02:27.627 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:27.632 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:27.636
2025-07-01 03:02:27.641 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:27.646 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:27.651 alo = 225, ahi = 1101
2025-07-01 03:02:27.656 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:27.661 blo = 225, bhi = 1101
2025-07-01 03:02:27.665
2025-07-01 03:02:27.671 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:27.675 r"""
2025-07-01 03:02:27.680 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:27.685 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:27.689 synch point, and intraline difference marking is done on the
2025-07-01 03:02:27.694 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:27.699
2025-07-01 03:02:27.704 Example:
2025-07-01 03:02:27.708
2025-07-01 03:02:27.712 >>> d = Differ()
2025-07-01 03:02:27.717 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:27.722 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:27.726 >>> print(''.join(results), end="")
2025-07-01 03:02:27.731 - abcDefghiJkl
2025-07-01 03:02:27.740 + abcdefGhijkl
2025-07-01 03:02:27.748 """
2025-07-01 03:02:27.753
2025-07-01 03:02:27.758 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:27.762 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:27.767 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:27.775 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:27.787 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:27.795
2025-07-01 03:02:27.801 # search for the pair that matches best without being identical
2025-07-01 03:02:27.807 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:27.812 # on junk -- unless we have to)
2025-07-01 03:02:27.817 for j in range(blo, bhi):
2025-07-01 03:02:27.822 bj = b[j]
2025-07-01 03:02:27.827 cruncher.set_seq2(bj)
2025-07-01 03:02:27.832 for i in range(alo, ahi):
2025-07-01 03:02:27.836 ai = a[i]
2025-07-01 03:02:27.840 if ai == bj:
2025-07-01 03:02:27.845 if eqi is None:
2025-07-01 03:02:27.849 eqi, eqj = i, j
2025-07-01 03:02:27.854 continue
2025-07-01 03:02:27.858 cruncher.set_seq1(ai)
2025-07-01 03:02:27.863 # computing similarity is expensive, so use the quick
2025-07-01 03:02:27.868 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:27.873 # compares by a factor of 3.
2025-07-01 03:02:27.877 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:27.882 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:27.887 # of the computation is cached by cruncher
2025-07-01 03:02:27.891 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:27.896 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:27.900 cruncher.ratio() > best_ratio:
2025-07-01 03:02:27.906 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:27.911 if best_ratio < cutoff:
2025-07-01 03:02:27.916 # no non-identical "pretty close" pair
2025-07-01 03:02:27.920 if eqi is None:
2025-07-01 03:02:27.925 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:27.930 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:27.935 return
2025-07-01 03:02:27.939 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:27.944 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:27.948 else:
2025-07-01 03:02:27.952 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:27.957 eqi = None
2025-07-01 03:02:27.961
2025-07-01 03:02:27.966 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:27.970 # identical
2025-07-01 03:02:27.974
2025-07-01 03:02:27.978 # pump out diffs from before the synch point
2025-07-01 03:02:27.983 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:27.987
2025-07-01 03:02:27.992 # do intraline marking on the synch pair
2025-07-01 03:02:27.996 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:28.000 if eqi is None:
2025-07-01 03:02:28.005 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:28.010 atags = btags = ""
2025-07-01 03:02:28.015 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:28.019 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:28.024 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:28.029 if tag == 'replace':
2025-07-01 03:02:28.033 atags += '^' * la
2025-07-01 03:02:28.038 btags += '^' * lb
2025-07-01 03:02:28.042 elif tag == 'delete':
2025-07-01 03:02:28.046 atags += '-' * la
2025-07-01 03:02:28.051 elif tag == 'insert':
2025-07-01 03:02:28.055 btags += '+' * lb
2025-07-01 03:02:28.060 elif tag == 'equal':
2025-07-01 03:02:28.064 atags += ' ' * la
2025-07-01 03:02:28.070 btags += ' ' * lb
2025-07-01 03:02:28.074 else:
2025-07-01 03:02:28.079 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:28.084 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:28.090 else:
2025-07-01 03:02:28.096 # the synch pair is identical
2025-07-01 03:02:28.101 yield '  ' + aelt
2025-07-01 03:02:28.106
2025-07-01 03:02:28.110 # pump out diffs from after the synch point
2025-07-01 03:02:28.116 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:28.123
2025-07-01 03:02:28.129 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:28.138 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:28.144
2025-07-01 03:02:28.150 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:28.155 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:28.159 alo = 226, ahi = 1101
2025-07-01 03:02:28.165 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:28.171 blo = 226, bhi = 1101
2025-07-01 03:02:28.175
2025-07-01 03:02:28.181 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:28.186 g = []
2025-07-01 03:02:28.192 if alo < ahi:
2025-07-01 03:02:28.198 if blo < bhi:
2025-07-01 03:02:28.204 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:28.210 else:
2025-07-01 03:02:28.215 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:28.219 elif blo < bhi:
2025-07-01 03:02:28.224 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:28.229
2025-07-01 03:02:28.234 >       yield from g
2025-07-01 03:02:28.238
2025-07-01 03:02:28.243 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:28.247 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:28.251
2025-07-01 03:02:28.256 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:28.261 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:28.265 alo = 226, ahi = 1101
2025-07-01 03:02:28.270 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:28.274 blo = 226, bhi = 1101
2025-07-01 03:02:28.279
2025-07-01 03:02:28.283 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:28.287 r"""
2025-07-01 03:02:28.292 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:28.296 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:28.301 synch point, and intraline difference marking is done on the
2025-07-01 03:02:28.305 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:28.309
2025-07-01 03:02:28.313 Example:
2025-07-01 03:02:28.318
2025-07-01 03:02:28.322 >>> d = Differ()
2025-07-01 03:02:28.326 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:28.330 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:28.335 >>> print(''.join(results), end="")
2025-07-01 03:02:28.339 - abcDefghiJkl
2025-07-01 03:02:28.347 + abcdefGhijkl
2025-07-01 03:02:28.356 """
2025-07-01 03:02:28.360
2025-07-01 03:02:28.364 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:28.369 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:28.373 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:28.377 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:28.382 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:28.386
2025-07-01 03:02:28.390 # search for the pair that matches best without being identical
2025-07-01 03:02:28.395 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:28.399 # on junk -- unless we have to)
2025-07-01 03:02:28.403 for j in range(blo, bhi):
2025-07-01 03:02:28.408 bj = b[j]
2025-07-01 03:02:28.412 cruncher.set_seq2(bj)
2025-07-01 03:02:28.416 for i in range(alo, ahi):
2025-07-01 03:02:28.420 ai = a[i]
2025-07-01 03:02:28.425 if ai == bj:
2025-07-01 03:02:28.429 if eqi is None:
2025-07-01 03:02:28.433 eqi, eqj = i, j
2025-07-01 03:02:28.437 continue
2025-07-01 03:02:28.442 cruncher.set_seq1(ai)
2025-07-01 03:02:28.446 # computing similarity is expensive, so use the quick
2025-07-01 03:02:28.450 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:28.454 # compares by a factor of 3.
2025-07-01 03:02:28.459 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:28.463 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:28.467 # of the computation is cached by cruncher
2025-07-01 03:02:28.472 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:28.476 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:28.480 cruncher.ratio() > best_ratio:
2025-07-01 03:02:28.484 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:28.489 if best_ratio < cutoff:
2025-07-01 03:02:28.493 # no non-identical "pretty close" pair
2025-07-01 03:02:28.497 if eqi is None:
2025-07-01 03:02:28.502 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:28.506 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:28.510 return
2025-07-01 03:02:28.514 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:28.519 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:28.523 else:
2025-07-01 03:02:28.527 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:28.531 eqi = None
2025-07-01 03:02:28.536
2025-07-01 03:02:28.540 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:28.544 # identical
2025-07-01 03:02:28.549
2025-07-01 03:02:28.553 # pump out diffs from before the synch point
2025-07-01 03:02:28.558 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:28.562
2025-07-01 03:02:28.566 # do intraline marking on the synch pair
2025-07-01 03:02:28.570 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:28.575 if eqi is None:
2025-07-01 03:02:28.580 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:28.584 atags = btags = ""
2025-07-01 03:02:28.589 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:28.594 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:28.600 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:28.605 if tag == 'replace':
2025-07-01 03:02:28.610 atags += '^' * la
2025-07-01 03:02:28.614 btags += '^' * lb
2025-07-01 03:02:28.619 elif tag == 'delete':
2025-07-01 03:02:28.623 atags += '-' * la
2025-07-01 03:02:28.627 elif tag == 'insert':
2025-07-01 03:02:28.632 btags += '+' * lb
2025-07-01 03:02:28.636 elif tag == 'equal':
2025-07-01 03:02:28.641 atags += ' ' * la
2025-07-01 03:02:28.645 btags += ' ' * lb
2025-07-01 03:02:28.649 else:
2025-07-01 03:02:28.654 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:28.659 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:28.663 else:
2025-07-01 03:02:28.667 # the synch pair is identical
2025-07-01 03:02:28.672 yield '  ' + aelt
2025-07-01 03:02:28.676
2025-07-01 03:02:28.681 # pump out diffs from after the synch point
2025-07-01 03:02:28.686 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:28.690
2025-07-01 03:02:28.695 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:28.700 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:28.705
2025-07-01 03:02:28.709 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:28.714 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:28.718 alo = 227, ahi = 1101
2025-07-01 03:02:28.723 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:28.727 blo = 227, bhi = 1101
2025-07-01 03:02:28.732
2025-07-01 03:02:28.737 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:28.741 g = []
2025-07-01 03:02:28.745 if alo < ahi:
2025-07-01 03:02:28.750 if blo < bhi:
2025-07-01 03:02:28.754 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:28.758 else:
2025-07-01 03:02:28.763 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:28.767 elif blo < bhi:
2025-07-01 03:02:28.772 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:28.776
2025-07-01 03:02:28.780 >       yield from g
2025-07-01 03:02:28.785
2025-07-01 03:02:28.789 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:28.793 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:28.798
2025-07-01 03:02:28.802 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:28.807 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:28.812 alo = 227, ahi = 1101
2025-07-01 03:02:28.816 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:28.821 blo = 227, bhi = 1101
2025-07-01 03:02:28.825
2025-07-01 03:02:28.830 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:28.834 r"""
2025-07-01 03:02:28.838 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:28.843 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:28.847 synch point, and intraline difference marking is done on the
2025-07-01 03:02:28.852 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:28.856
2025-07-01 03:02:28.861 Example:
2025-07-01 03:02:28.865
2025-07-01 03:02:28.870 >>> d = Differ()
2025-07-01 03:02:28.874 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:28.879 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:28.883 >>> print(''.join(results), end="")
2025-07-01 03:02:28.887 - abcDefghiJkl
2025-07-01 03:02:28.896 + abcdefGhijkl
2025-07-01 03:02:28.904 """
2025-07-01 03:02:28.909
2025-07-01 03:02:28.914 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:28.920 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:28.925 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:28.930 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:28.936 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:28.942
2025-07-01 03:02:28.947 # search for the pair that matches best without being identical
2025-07-01 03:02:28.952 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:28.956 # on junk -- unless we have to)
2025-07-01 03:02:28.960 for j in range(blo, bhi):
2025-07-01 03:02:28.965 bj = b[j]
2025-07-01 03:02:28.970 cruncher.set_seq2(bj)
2025-07-01 03:02:28.974 for i in range(alo, ahi):
2025-07-01 03:02:28.979 ai = a[i]
2025-07-01 03:02:28.983 if ai == bj:
2025-07-01 03:02:28.987 if eqi is None:
2025-07-01 03:02:28.992 eqi, eqj = i, j
2025-07-01 03:02:28.996 continue
2025-07-01 03:02:29.000 cruncher.set_seq1(ai)
2025-07-01 03:02:29.004 # computing similarity is expensive, so use the quick
2025-07-01 03:02:29.009 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:29.013 # compares by a factor of 3.
2025-07-01 03:02:29.017 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:29.022 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:29.026 # of the computation is cached by cruncher
2025-07-01 03:02:29.031 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:29.035 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:29.039 cruncher.ratio() > best_ratio:
2025-07-01 03:02:29.044 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:29.049 if best_ratio < cutoff:
2025-07-01 03:02:29.053 # no non-identical "pretty close" pair
2025-07-01 03:02:29.057 if eqi is None:
2025-07-01 03:02:29.062 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:29.066 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:29.071 return
2025-07-01 03:02:29.075 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:29.080 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:29.084 else:
2025-07-01 03:02:29.088 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:29.093 eqi = None
2025-07-01 03:02:29.097
2025-07-01 03:02:29.102 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:29.106 # identical
2025-07-01 03:02:29.110
2025-07-01 03:02:29.115 # pump out diffs from before the synch point
2025-07-01 03:02:29.120 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:29.124
2025-07-01 03:02:29.129 # do intraline marking on the synch pair
2025-07-01 03:02:29.133 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:29.138 if eqi is None:
2025-07-01 03:02:29.142 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:29.146 atags = btags = ""
2025-07-01 03:02:29.151 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:29.156 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:29.160 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:29.165 if tag == 'replace':
2025-07-01 03:02:29.169 atags += '^' * la
2025-07-01 03:02:29.174 btags += '^' * lb
2025-07-01 03:02:29.178 elif tag == 'delete':
2025-07-01 03:02:29.183 atags += '-' * la
2025-07-01 03:02:29.187 elif tag == 'insert':
2025-07-01 03:02:29.191 btags += '+' * lb
2025-07-01 03:02:29.196 elif tag == 'equal':
2025-07-01 03:02:29.200 atags += ' ' * la
2025-07-01 03:02:29.205 btags += ' ' * lb
2025-07-01 03:02:29.209 else:
2025-07-01 03:02:29.213 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:29.218 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:29.222 else:
2025-07-01 03:02:29.226 # the synch pair is identical
2025-07-01 03:02:29.231 yield '  ' + aelt
2025-07-01 03:02:29.235
2025-07-01 03:02:29.239 # pump out diffs from after the synch point
2025-07-01 03:02:29.244 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:29.248
2025-07-01 03:02:29.252 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:29.257 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:29.261
2025-07-01 03:02:29.265 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:29.270 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:29.274 alo = 228, ahi = 1101
2025-07-01 03:02:29.279 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:29.283 blo = 228, bhi = 1101
2025-07-01 03:02:29.287
2025-07-01 03:02:29.292 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:29.296 g = []
2025-07-01 03:02:29.301 if alo < ahi:
2025-07-01 03:02:29.305 if blo < bhi:
2025-07-01 03:02:29.310 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:29.314 else:
2025-07-01 03:02:29.318 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:29.323 elif blo < bhi:
2025-07-01 03:02:29.327 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:29.331
2025-07-01 03:02:29.335 >       yield from g
2025-07-01 03:02:29.340
2025-07-01 03:02:29.344 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:29.348 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:29.353
2025-07-01 03:02:29.357 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:29.362 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:29.366 alo = 228, ahi = 1101
2025-07-01 03:02:29.371 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:29.376 blo = 228, bhi = 1101
2025-07-01 03:02:29.380
2025-07-01 03:02:29.384 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:29.389 r"""
2025-07-01 03:02:29.393 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:29.397 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:29.402 synch point, and intraline difference marking is done on the
2025-07-01 03:02:29.406 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:29.411
2025-07-01 03:02:29.415 Example:
2025-07-01 03:02:29.419
2025-07-01 03:02:29.423 >>> d = Differ()
2025-07-01 03:02:29.427 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:29.432 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:29.437 >>> print(''.join(results), end="")
2025-07-01 03:02:29.441 - abcDefghiJkl
2025-07-01 03:02:29.449 + abcdefGhijkl
2025-07-01 03:02:29.459 """
2025-07-01 03:02:29.463
2025-07-01 03:02:29.467 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:29.472 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:29.476 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:29.481 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:29.485 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:29.489
2025-07-01 03:02:29.493 # search for the pair that matches best without being identical
2025-07-01 03:02:29.498 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:29.502 # on junk -- unless we have to)
2025-07-01 03:02:29.506 for j in range(blo, bhi):
2025-07-01 03:02:29.511 bj = b[j]
2025-07-01 03:02:29.515 cruncher.set_seq2(bj)
2025-07-01 03:02:29.519 for i in range(alo, ahi):
2025-07-01 03:02:29.524 ai = a[i]
2025-07-01 03:02:29.528 if ai == bj:
2025-07-01 03:02:29.532 if eqi is None:
2025-07-01 03:02:29.537 eqi, eqj = i, j
2025-07-01 03:02:29.541 continue
2025-07-01 03:02:29.545 cruncher.set_seq1(ai)
2025-07-01 03:02:29.549 # computing similarity is expensive, so use the quick
2025-07-01 03:02:29.554 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:29.558 # compares by a factor of 3.
2025-07-01 03:02:29.562 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:29.567 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:29.572 # of the computation is cached by cruncher
2025-07-01 03:02:29.576 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:29.580 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:29.585 cruncher.ratio() > best_ratio:
2025-07-01 03:02:29.589 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:29.594 if best_ratio < cutoff:
2025-07-01 03:02:29.598 # no non-identical "pretty close" pair
2025-07-01 03:02:29.602 if eqi is None:
2025-07-01 03:02:29.607 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:29.611 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:29.616 return
2025-07-01 03:02:29.620 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:29.625 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:29.629 else:
2025-07-01 03:02:29.633 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:29.638 eqi = None
2025-07-01 03:02:29.642
2025-07-01 03:02:29.647 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:29.651 # identical
2025-07-01 03:02:29.655
2025-07-01 03:02:29.660 # pump out diffs from before the synch point
2025-07-01 03:02:29.664 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:29.668
2025-07-01 03:02:29.673 # do intraline marking on the synch pair
2025-07-01 03:02:29.677 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:29.681 if eqi is None:
2025-07-01 03:02:29.686 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:29.690 atags = btags = ""
2025-07-01 03:02:29.695 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:29.701 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:29.705 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:29.709 if tag == 'replace':
2025-07-01 03:02:29.714 atags += '^' * la
2025-07-01 03:02:29.718 btags += '^' * lb
2025-07-01 03:02:29.722 elif tag == 'delete':
2025-07-01 03:02:29.726 atags += '-' * la
2025-07-01 03:02:29.731 elif tag == 'insert':
2025-07-01 03:02:29.735 btags += '+' * lb
2025-07-01 03:02:29.740 elif tag == 'equal':
2025-07-01 03:02:29.744 atags += ' ' * la
2025-07-01 03:02:29.748 btags += ' ' * lb
2025-07-01 03:02:29.752 else:
2025-07-01 03:02:29.757 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:29.761 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:29.767 else:
2025-07-01 03:02:29.773 # the synch pair is identical
2025-07-01 03:02:29.779 yield '  ' + aelt
2025-07-01 03:02:29.786
2025-07-01 03:02:29.792 # pump out diffs from after the synch point
2025-07-01 03:02:29.799 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:29.806
2025-07-01 03:02:29.812 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:29.818 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:29.824
2025-07-01 03:02:29.830 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:29.836 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:29.842 alo = 229, ahi = 1101
2025-07-01 03:02:29.848 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:29.854 blo = 229, bhi = 1101
2025-07-01 03:02:29.859
2025-07-01 03:02:29.865 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:29.873 g = []
2025-07-01 03:02:29.879 if alo < ahi:
2025-07-01 03:02:29.885 if blo < bhi:
2025-07-01 03:02:29.892 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:29.900 else:
2025-07-01 03:02:29.908 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:29.914 elif blo < bhi:
2025-07-01 03:02:29.919 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:29.924
2025-07-01 03:02:29.928 >       yield from g
2025-07-01 03:02:29.933
2025-07-01 03:02:29.938 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:29.942 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:29.947
2025-07-01 03:02:29.953 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:29.958 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:29.963 alo = 229, ahi = 1101
2025-07-01 03:02:29.968 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:29.973 blo = 229, bhi = 1101
2025-07-01 03:02:29.977
2025-07-01 03:02:29.982 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:29.986 r"""
2025-07-01 03:02:29.991 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:29.996 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:30.000 synch point, and intraline difference marking is done on the
2025-07-01 03:02:30.006 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:30.012
2025-07-01 03:02:30.017 Example:
2025-07-01 03:02:30.022
2025-07-01 03:02:30.028 >>> d = Differ()
2025-07-01 03:02:30.033 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:30.038 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:30.042 >>> print(''.join(results), end="")
2025-07-01 03:02:30.047 - abcDefghiJkl
2025-07-01 03:02:30.055 + abcdefGhijkl
2025-07-01 03:02:30.064 """
2025-07-01 03:02:30.068
2025-07-01 03:02:30.072 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:30.077 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:30.081 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:30.086 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:30.090 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:30.094
2025-07-01 03:02:30.099 # search for the pair that matches best without being identical
2025-07-01 03:02:30.103 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:30.108 # on junk -- unless we have to)
2025-07-01 03:02:30.112 for j in range(blo, bhi):
2025-07-01 03:02:30.116 bj = b[j]
2025-07-01 03:02:30.120 cruncher.set_seq2(bj)
2025-07-01 03:02:30.125 for i in range(alo, ahi):
2025-07-01 03:02:30.129 ai = a[i]
2025-07-01 03:02:30.133 if ai == bj:
2025-07-01 03:02:30.138 if eqi is None:
2025-07-01 03:02:30.142 eqi, eqj = i, j
2025-07-01 03:02:30.147 continue
2025-07-01 03:02:30.151 cruncher.set_seq1(ai)
2025-07-01 03:02:30.155 # computing similarity is expensive, so use the quick
2025-07-01 03:02:30.160 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:30.164 # compares by a factor of 3.
2025-07-01 03:02:30.168 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:30.173 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:30.177 # of the computation is cached by cruncher
2025-07-01 03:02:30.182 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:30.186 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:30.190 cruncher.ratio() > best_ratio:
2025-07-01 03:02:30.195 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:30.199 if best_ratio < cutoff:
2025-07-01 03:02:30.203 # no non-identical "pretty close" pair
2025-07-01 03:02:30.208 if eqi is None:
2025-07-01 03:02:30.212 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:30.218 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:30.222 return
2025-07-01 03:02:30.227 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:30.231 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:30.235 else:
2025-07-01 03:02:30.240 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:30.244 eqi = None
2025-07-01 03:02:30.248
2025-07-01 03:02:30.253 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:30.257 # identical
2025-07-01 03:02:30.261
2025-07-01 03:02:30.265 # pump out diffs from before the synch point
2025-07-01 03:02:30.270 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:30.274
2025-07-01 03:02:30.278 # do intraline marking on the synch pair
2025-07-01 03:02:30.283 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:30.287 if eqi is None:
2025-07-01 03:02:30.292 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:30.296 atags = btags = ""
2025-07-01 03:02:30.300 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:30.305 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:30.310 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:30.314 if tag == 'replace':
2025-07-01 03:02:30.318 atags += '^' * la
2025-07-01 03:02:30.322 btags += '^' * lb
2025-07-01 03:02:30.327 elif tag == 'delete':
2025-07-01 03:02:30.331 atags += '-' * la
2025-07-01 03:02:30.336 elif tag == 'insert':
2025-07-01 03:02:30.340 btags += '+' * lb
2025-07-01 03:02:30.345 elif tag == 'equal':
2025-07-01 03:02:30.349 atags += ' ' * la
2025-07-01 03:02:30.353 btags += ' ' * lb
2025-07-01 03:02:30.358 else:
2025-07-01 03:02:30.362 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:30.367 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:30.371 else:
2025-07-01 03:02:30.376 # the synch pair is identical
2025-07-01 03:02:30.380 yield '  ' + aelt
2025-07-01 03:02:30.384
2025-07-01 03:02:30.389 # pump out diffs from after the synch point
2025-07-01 03:02:30.393 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:30.398
2025-07-01 03:02:30.402 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:30.406 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:30.411
2025-07-01 03:02:30.415 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:30.419 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:30.424 alo = 230, ahi = 1101
2025-07-01 03:02:30.429 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:30.433 blo = 230, bhi = 1101
2025-07-01 03:02:30.437
2025-07-01 03:02:30.442 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:30.446 g = []
2025-07-01 03:02:30.451 if alo < ahi:
2025-07-01 03:02:30.455 if blo < bhi:
2025-07-01 03:02:30.459 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:30.464 else:
2025-07-01 03:02:30.468 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:30.473 elif blo < bhi:
2025-07-01 03:02:30.477 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:30.481
2025-07-01 03:02:30.487 >       yield from g
2025-07-01 03:02:30.491
2025-07-01 03:02:30.496 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:30.501 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:30.505
2025-07-01 03:02:30.510 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:30.514 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:30.520 alo = 230, ahi = 1101
2025-07-01 03:02:30.524 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:30.529 blo = 230, bhi = 1101
2025-07-01 03:02:30.533
2025-07-01 03:02:30.538 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:30.543 r"""
2025-07-01 03:02:30.548 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:30.553 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:30.557 synch point, and intraline difference marking is done on the
2025-07-01 03:02:30.562 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:30.566
2025-07-01 03:02:30.570 Example:
2025-07-01 03:02:30.574
2025-07-01 03:02:30.579 >>> d = Differ()
2025-07-01 03:02:30.584 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:30.588 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:30.593 >>> print(''.join(results), end="")
2025-07-01 03:02:30.598 - abcDefghiJkl
2025-07-01 03:02:30.607 + abcdefGhijkl
2025-07-01 03:02:30.615 """
2025-07-01 03:02:30.620
2025-07-01 03:02:30.624 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:30.629 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:30.633 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:30.638 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:30.643 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:30.647
2025-07-01 03:02:30.652 # search for the pair that matches best without being identical
2025-07-01 03:02:30.657 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:30.662 # on junk -- unless we have to)
2025-07-01 03:02:30.667 for j in range(blo, bhi):
2025-07-01 03:02:30.671 bj = b[j]
2025-07-01 03:02:30.675 cruncher.set_seq2(bj)
2025-07-01 03:02:30.679 for i in range(alo, ahi):
2025-07-01 03:02:30.684 ai = a[i]
2025-07-01 03:02:30.688 if ai == bj:
2025-07-01 03:02:30.692 if eqi is None:
2025-07-01 03:02:30.696 eqi, eqj = i, j
2025-07-01 03:02:30.701 continue
2025-07-01 03:02:30.705 cruncher.set_seq1(ai)
2025-07-01 03:02:30.709 # computing similarity is expensive, so use the quick
2025-07-01 03:02:30.714 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:30.719 # compares by a factor of 3.
2025-07-01 03:02:30.723 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:30.727 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:30.732 # of the computation is cached by cruncher
2025-07-01 03:02:30.736 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:30.741 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:30.745 cruncher.ratio() > best_ratio:
2025-07-01 03:02:30.749 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:30.754 if best_ratio < cutoff:
2025-07-01 03:02:30.758 # no non-identical "pretty close" pair
2025-07-01 03:02:30.763 if eqi is None:
2025-07-01 03:02:30.767 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:30.771 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:30.776 return
2025-07-01 03:02:30.780 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:30.785 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:30.789 else:
2025-07-01 03:02:30.794 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:30.798 eqi = None
2025-07-01 03:02:30.802
2025-07-01 03:02:30.807 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:30.811 # identical
2025-07-01 03:02:30.815
2025-07-01 03:02:30.820 # pump out diffs from before the synch point
2025-07-01 03:02:30.824 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:30.829
2025-07-01 03:02:30.834 # do intraline marking on the synch pair
2025-07-01 03:02:30.838 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:30.842 if eqi is None:
2025-07-01 03:02:30.847 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:30.851 atags = btags = ""
2025-07-01 03:02:30.856 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:30.860 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:30.865 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:30.869 if tag == 'replace':
2025-07-01 03:02:30.874 atags += '^' * la
2025-07-01 03:02:30.878 btags += '^' * lb
2025-07-01 03:02:30.883 elif tag == 'delete':
2025-07-01 03:02:30.887 atags += '-' * la
2025-07-01 03:02:30.891 elif tag == 'insert':
2025-07-01 03:02:30.896 btags += '+' * lb
2025-07-01 03:02:30.900 elif tag == 'equal':
2025-07-01 03:02:30.905 atags += ' ' * la
2025-07-01 03:02:30.909 btags += ' ' * lb
2025-07-01 03:02:30.914 else:
2025-07-01 03:02:30.919 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:30.924 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:30.928 else:
2025-07-01 03:02:30.933 # the synch pair is identical
2025-07-01 03:02:30.937 yield '  ' + aelt
2025-07-01 03:02:30.942
2025-07-01 03:02:30.946 # pump out diffs from after the synch point
2025-07-01 03:02:30.950 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:30.955
2025-07-01 03:02:30.959 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:30.963 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:30.968
2025-07-01 03:02:30.972 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:30.977 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:30.981 alo = 231, ahi = 1101
2025-07-01 03:02:30.986 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:30.991 blo = 231, bhi = 1101
2025-07-01 03:02:30.996
2025-07-01 03:02:31.000 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:31.005 g = []
2025-07-01 03:02:31.009 if alo < ahi:
2025-07-01 03:02:31.014 if blo < bhi:
2025-07-01 03:02:31.018 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:31.023 else:
2025-07-01 03:02:31.027 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:31.032 elif blo < bhi:
2025-07-01 03:02:31.036 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:31.040
2025-07-01 03:02:31.045 >       yield from g
2025-07-01 03:02:31.049
2025-07-01 03:02:31.053 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:31.058 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:31.062
2025-07-01 03:02:31.066 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:31.070 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:31.075 alo = 231, ahi = 1101
2025-07-01 03:02:31.080 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:31.084 blo = 231, bhi = 1101
2025-07-01 03:02:31.088
2025-07-01 03:02:31.092 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:31.097 r"""
2025-07-01 03:02:31.101 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:31.105 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:31.110 synch point, and intraline difference marking is done on the
2025-07-01 03:02:31.114 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:31.119
2025-07-01 03:02:31.123 Example:
2025-07-01 03:02:31.127
2025-07-01 03:02:31.131 >>> d = Differ()
2025-07-01 03:02:31.136 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:31.140 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:31.144 >>> print(''.join(results), end="")
2025-07-01 03:02:31.149 - abcDefghiJkl
2025-07-01 03:02:31.157 + abcdefGhijkl
2025-07-01 03:02:31.165 """
2025-07-01 03:02:31.170
2025-07-01 03:02:31.174 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:31.178 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:31.183 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:31.187 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:31.191 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:31.196
2025-07-01 03:02:31.200 # search for the pair that matches best without being identical
2025-07-01 03:02:31.204 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:31.209 # on junk -- unless we have to)
2025-07-01 03:02:31.213 for j in range(blo, bhi):
2025-07-01 03:02:31.218 bj = b[j]
2025-07-01 03:02:31.222 cruncher.set_seq2(bj)
2025-07-01 03:02:31.226 for i in range(alo, ahi):
2025-07-01 03:02:31.231 ai = a[i]
2025-07-01 03:02:31.235 if ai == bj:
2025-07-01 03:02:31.239 if eqi is None:
2025-07-01 03:02:31.244 eqi, eqj = i, j
2025-07-01 03:02:31.250 continue
2025-07-01 03:02:31.254 cruncher.set_seq1(ai)
2025-07-01 03:02:31.259 # computing similarity is expensive, so use the quick
2025-07-01 03:02:31.263 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:31.268 # compares by a factor of 3.
2025-07-01 03:02:31.273 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:31.277 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:31.282 # of the computation is cached by cruncher
2025-07-01 03:02:31.286 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:31.291 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:31.297 cruncher.ratio() > best_ratio:
2025-07-01 03:02:31.302 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:31.307 if best_ratio < cutoff:
2025-07-01 03:02:31.312 # no non-identical "pretty close" pair
2025-07-01 03:02:31.317 if eqi is None:
2025-07-01 03:02:31.322 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:31.327 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:31.331 return
2025-07-01 03:02:31.336 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:31.342 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:31.348 else:
2025-07-01 03:02:31.354 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:31.360 eqi = None
2025-07-01 03:02:31.365
2025-07-01 03:02:31.370 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:31.375 # identical
2025-07-01 03:02:31.380
2025-07-01 03:02:31.385 # pump out diffs from before the synch point
2025-07-01 03:02:31.391 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:31.396
2025-07-01 03:02:31.401 # do intraline marking on the synch pair
2025-07-01 03:02:31.405 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:31.410 if eqi is None:
2025-07-01 03:02:31.414 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:31.419 atags = btags = ""
2025-07-01 03:02:31.423 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:31.428 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:31.432 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:31.437 if tag == 'replace':
2025-07-01 03:02:31.441 atags += '^' * la
2025-07-01 03:02:31.446 btags += '^' * lb
2025-07-01 03:02:31.450 elif tag == 'delete':
2025-07-01 03:02:31.455 atags += '-' * la
2025-07-01 03:02:31.459 elif tag == 'insert':
2025-07-01 03:02:31.464 btags += '+' * lb
2025-07-01 03:02:31.469 elif tag == 'equal':
2025-07-01 03:02:31.473 atags += ' ' * la
2025-07-01 03:02:31.478 btags += ' ' * lb
2025-07-01 03:02:31.482 else:
2025-07-01 03:02:31.487 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:31.491 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:31.496 else:
2025-07-01 03:02:31.500 # the synch pair is identical
2025-07-01 03:02:31.505 yield '  ' + aelt
2025-07-01 03:02:31.509
2025-07-01 03:02:31.514 # pump out diffs from after the synch point
2025-07-01 03:02:31.519 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:31.523
2025-07-01 03:02:31.528 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:31.532 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:31.537
2025-07-01 03:02:31.541 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:31.546 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:31.550 alo = 232, ahi = 1101
2025-07-01 03:02:31.555 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:31.560 blo = 232, bhi = 1101
2025-07-01 03:02:31.564
2025-07-01 03:02:31.575 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:31.585 g = []
2025-07-01 03:02:31.589 if alo < ahi:
2025-07-01 03:02:31.594 if blo < bhi:
2025-07-01 03:02:31.599 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:31.604 else:
2025-07-01 03:02:31.608 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:31.613 elif blo < bhi:
2025-07-01 03:02:31.618 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:31.622
2025-07-01 03:02:31.626 >       yield from g
2025-07-01 03:02:31.631
2025-07-01 03:02:31.635 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:31.640 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:31.645
2025-07-01 03:02:31.649 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:31.655 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:31.659 alo = 232, ahi = 1101
2025-07-01 03:02:31.665 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:31.670 blo = 232, bhi = 1101
2025-07-01 03:02:31.676
2025-07-01 03:02:31.682 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:31.688 r"""
2025-07-01 03:02:31.693 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:31.699 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:31.704 synch point, and intraline difference marking is done on the
2025-07-01 03:02:31.710 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:31.716
2025-07-01 03:02:31.722 Example:
2025-07-01 03:02:31.726
2025-07-01 03:02:31.731 >>> d = Differ()
2025-07-01 03:02:31.735 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:31.739 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:31.744 >>> print(''.join(results), end="")
2025-07-01 03:02:31.748 - abcDefghiJkl
2025-07-01 03:02:31.757 + abcdefGhijkl
2025-07-01 03:02:31.766 """
2025-07-01 03:02:31.770
2025-07-01 03:02:31.774 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:31.779 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:31.784 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:31.788 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:31.793 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:31.797
2025-07-01 03:02:31.804 # search for the pair that matches best without being identical
2025-07-01 03:02:31.809 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:31.814 # on junk -- unless we have to)
2025-07-01 03:02:31.819 for j in range(blo, bhi):
2025-07-01 03:02:31.823 bj = b[j]
2025-07-01 03:02:31.828 cruncher.set_seq2(bj)
2025-07-01 03:02:31.834 for i in range(alo, ahi):
2025-07-01 03:02:31.839 ai = a[i]
2025-07-01 03:02:31.844 if ai == bj:
2025-07-01 03:02:31.849 if eqi is None:
2025-07-01 03:02:31.854 eqi, eqj = i, j
2025-07-01 03:02:31.859 continue
2025-07-01 03:02:31.865 cruncher.set_seq1(ai)
2025-07-01 03:02:31.870 # computing similarity is expensive, so use the quick
2025-07-01 03:02:31.876 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:31.882 # compares by a factor of 3.
2025-07-01 03:02:31.886 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:31.891 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:31.897 # of the computation is cached by cruncher
2025-07-01 03:02:31.903 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:31.907 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:31.912 cruncher.ratio() > best_ratio:
2025-07-01 03:02:31.916 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:31.920 if best_ratio < cutoff:
2025-07-01 03:02:31.926 # no non-identical "pretty close" pair
2025-07-01 03:02:31.930 if eqi is None:
2025-07-01 03:02:31.935 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:31.939 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:31.943 return
2025-07-01 03:02:31.948 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:31.952 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:31.957 else:
2025-07-01 03:02:31.961 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:31.965 eqi = None
2025-07-01 03:02:31.970
2025-07-01 03:02:31.974 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:31.978 # identical
2025-07-01 03:02:31.983
2025-07-01 03:02:31.987 # pump out diffs from before the synch point
2025-07-01 03:02:31.991 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:31.996
2025-07-01 03:02:32.000 # do intraline marking on the synch pair
2025-07-01 03:02:32.005 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:32.009 if eqi is None:
2025-07-01 03:02:32.015 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:32.020 atags = btags = ""
2025-07-01 03:02:32.025 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:32.030 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:32.034 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:32.039 if tag == 'replace':
2025-07-01 03:02:32.043 atags += '^' * la
2025-07-01 03:02:32.048 btags += '^' * lb
2025-07-01 03:02:32.053 elif tag == 'delete':
2025-07-01 03:02:32.058 atags += '-' * la
2025-07-01 03:02:32.063 elif tag == 'insert':
2025-07-01 03:02:32.071 btags += '+' * lb
2025-07-01 03:02:32.077 elif tag == 'equal':
2025-07-01 03:02:32.084 atags += ' ' * la
2025-07-01 03:02:32.094 btags += ' ' * lb
2025-07-01 03:02:32.116 else:
2025-07-01 03:02:32.126 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:32.146 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:32.165 else:
2025-07-01 03:02:32.185 # the synch pair is identical
2025-07-01 03:02:32.201 yield '  ' + aelt
2025-07-01 03:02:32.213
2025-07-01 03:02:32.238 # pump out diffs from after the synch point
2025-07-01 03:02:32.261 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:32.267
2025-07-01 03:02:32.273 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:32.281 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:32.287
2025-07-01 03:02:32.294 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:32.302 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:32.309 alo = 233, ahi = 1101
2025-07-01 03:02:32.316 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:32.322 blo = 233, bhi = 1101
2025-07-01 03:02:32.328
2025-07-01 03:02:32.334 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:32.340 g = []
2025-07-01 03:02:32.346 if alo < ahi:
2025-07-01 03:02:32.352 if blo < bhi:
2025-07-01 03:02:32.358 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:32.364 else:
2025-07-01 03:02:32.370 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:32.376 elif blo < bhi:
2025-07-01 03:02:32.382 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:32.389
2025-07-01 03:02:32.395 >       yield from g
2025-07-01 03:02:32.401
2025-07-01 03:02:32.407 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:32.413 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:32.419
2025-07-01 03:02:32.426 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:32.432 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:32.438 alo = 233, ahi = 1101
2025-07-01 03:02:32.445 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:32.450 blo = 233, bhi = 1101
2025-07-01 03:02:32.454
2025-07-01 03:02:32.459 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:32.463 r"""
2025-07-01 03:02:32.468 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:32.472 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:32.477 synch point, and intraline difference marking is done on the
2025-07-01 03:02:32.481 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:32.486
2025-07-01 03:02:32.491 Example:
2025-07-01 03:02:32.495
2025-07-01 03:02:32.499 >>> d = Differ()
2025-07-01 03:02:32.504 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:32.508 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:32.512 >>> print(''.join(results), end="")
2025-07-01 03:02:32.517 - abcDefghiJkl
2025-07-01 03:02:32.525 + abcdefGhijkl
2025-07-01 03:02:32.534 """
2025-07-01 03:02:32.538
2025-07-01 03:02:32.542 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:32.547 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:32.553 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:32.558 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:32.564 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:32.569
2025-07-01 03:02:32.575 # search for the pair that matches best without being identical
2025-07-01 03:02:32.581 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:32.586 # on junk -- unless we have to)
2025-07-01 03:02:32.593 for j in range(blo, bhi):
2025-07-01 03:02:32.599 bj = b[j]
2025-07-01 03:02:32.604 cruncher.set_seq2(bj)
2025-07-01 03:02:32.610 for i in range(alo, ahi):
2025-07-01 03:02:32.615 ai = a[i]
2025-07-01 03:02:32.620 if ai == bj:
2025-07-01 03:02:32.625 if eqi is None:
2025-07-01 03:02:32.629 eqi, eqj = i, j
2025-07-01 03:02:32.635 continue
2025-07-01 03:02:32.641 cruncher.set_seq1(ai)
2025-07-01 03:02:32.648 # computing similarity is expensive, so use the quick
2025-07-01 03:02:32.654 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:32.661 # compares by a factor of 3.
2025-07-01 03:02:32.668 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:32.674 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:32.681 # of the computation is cached by cruncher
2025-07-01 03:02:32.688 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:32.695 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:32.701 cruncher.ratio() > best_ratio:
2025-07-01 03:02:32.706 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:32.710 if best_ratio < cutoff:
2025-07-01 03:02:32.715 # no non-identical "pretty close" pair
2025-07-01 03:02:32.720 if eqi is None:
2025-07-01 03:02:32.724 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:32.729 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:32.734 return
2025-07-01 03:02:32.739 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:32.743 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:32.748 else:
2025-07-01 03:02:32.753 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:32.757 eqi = None
2025-07-01 03:02:32.762
2025-07-01 03:02:32.767 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:32.772 # identical
2025-07-01 03:02:32.776
2025-07-01 03:02:32.781 # pump out diffs from before the synch point
2025-07-01 03:02:32.785 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:32.790
2025-07-01 03:02:32.795 # do intraline marking on the synch pair
2025-07-01 03:02:32.799 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:32.804 if eqi is None:
2025-07-01 03:02:32.809 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:32.814 atags = btags = ""
2025-07-01 03:02:32.819 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:32.824 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:32.828 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:32.833 if tag == 'replace':
2025-07-01 03:02:32.838 atags += '^' * la
2025-07-01 03:02:32.843 btags += '^' * lb
2025-07-01 03:02:32.847 elif tag == 'delete':
2025-07-01 03:02:32.854 atags += '-' * la
2025-07-01 03:02:32.860 elif tag == 'insert':
2025-07-01 03:02:32.864 btags += '+' * lb
2025-07-01 03:02:32.869 elif tag == 'equal':
2025-07-01 03:02:32.873 atags += ' ' * la
2025-07-01 03:02:32.878 btags += ' ' * lb
2025-07-01 03:02:32.882 else:
2025-07-01 03:02:32.887 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:32.891 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:32.895 else:
2025-07-01 03:02:32.900 # the synch pair is identical
2025-07-01 03:02:32.904 yield '  ' + aelt
2025-07-01 03:02:32.909
2025-07-01 03:02:32.914 # pump out diffs from after the synch point
2025-07-01 03:02:32.918 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:32.923
2025-07-01 03:02:32.927 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:32.932 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:32.936
2025-07-01 03:02:32.941 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:32.946 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:32.950 alo = 234, ahi = 1101
2025-07-01 03:02:32.955 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:32.960 blo = 234, bhi = 1101
2025-07-01 03:02:32.969
2025-07-01 03:02:32.973 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:32.978 g = []
2025-07-01 03:02:32.986 if alo < ahi:
2025-07-01 03:02:32.991 if blo < bhi:
2025-07-01 03:02:32.996 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:33.000 else:
2025-07-01 03:02:33.006 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:33.013 elif blo < bhi:
2025-07-01 03:02:33.019 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:33.025
2025-07-01 03:02:33.031 >       yield from g
2025-07-01 03:02:33.037
2025-07-01 03:02:33.044 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:33.050 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:33.056
2025-07-01 03:02:33.063 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:33.069 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:33.075 alo = 234, ahi = 1101
2025-07-01 03:02:33.083 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:33.089 blo = 234, bhi = 1101
2025-07-01 03:02:33.095
2025-07-01 03:02:33.101 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:33.106 r"""
2025-07-01 03:02:33.113 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:33.118 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:33.123 synch point, and intraline difference marking is done on the
2025-07-01 03:02:33.127 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:33.131
2025-07-01 03:02:33.136 Example:
2025-07-01 03:02:33.140
2025-07-01 03:02:33.144 >>> d = Differ()
2025-07-01 03:02:33.149 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:33.153 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:33.158 >>> print(''.join(results), end="")
2025-07-01 03:02:33.163 - abcDefghiJkl
2025-07-01 03:02:33.172 + abcdefGhijkl
2025-07-01 03:02:33.182 """
2025-07-01 03:02:33.187
2025-07-01 03:02:33.192 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:33.197 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:33.202 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:33.207 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:33.212 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:33.216
2025-07-01 03:02:33.222 # search for the pair that matches best without being identical
2025-07-01 03:02:33.228 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:33.233 # on junk -- unless we have to)
2025-07-01 03:02:33.237 for j in range(blo, bhi):
2025-07-01 03:02:33.242 bj = b[j]
2025-07-01 03:02:33.246 cruncher.set_seq2(bj)
2025-07-01 03:02:33.251 for i in range(alo, ahi):
2025-07-01 03:02:33.256 ai = a[i]
2025-07-01 03:02:33.261 if ai == bj:
2025-07-01 03:02:33.265 if eqi is None:
2025-07-01 03:02:33.270 eqi, eqj = i, j
2025-07-01 03:02:33.275 continue
2025-07-01 03:02:33.280 cruncher.set_seq1(ai)
2025-07-01 03:02:33.284 # computing similarity is expensive, so use the quick
2025-07-01 03:02:33.290 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:33.295 # compares by a factor of 3.
2025-07-01 03:02:33.299 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:33.304 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:33.309 # of the computation is cached by cruncher
2025-07-01 03:02:33.313 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:33.318 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:33.323 cruncher.ratio() > best_ratio:
2025-07-01 03:02:33.327 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:33.332 if best_ratio < cutoff:
2025-07-01 03:02:33.337 # no non-identical "pretty close" pair
2025-07-01 03:02:33.341 if eqi is None:
2025-07-01 03:02:33.347 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:33.352 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:33.356 return
2025-07-01 03:02:33.361 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:33.367 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:33.371 else:
2025-07-01 03:02:33.376 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:33.381 eqi = None
2025-07-01 03:02:33.385
2025-07-01 03:02:33.391 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:33.396 # identical
2025-07-01 03:02:33.401
2025-07-01 03:02:33.406 # pump out diffs from before the synch point
2025-07-01 03:02:33.411 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:33.415
2025-07-01 03:02:33.420 # do intraline marking on the synch pair
2025-07-01 03:02:33.424 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:33.429 if eqi is None:
2025-07-01 03:02:33.433 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:33.438 atags = btags = ""
2025-07-01 03:02:33.442 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:33.447 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:33.451 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:33.456 if tag == 'replace':
2025-07-01 03:02:33.461 atags += '^' * la
2025-07-01 03:02:33.466 btags += '^' * lb
2025-07-01 03:02:33.471 elif tag == 'delete':
2025-07-01 03:02:33.475 atags += '-' * la
2025-07-01 03:02:33.480 elif tag == 'insert':
2025-07-01 03:02:33.484 btags += '+' * lb
2025-07-01 03:02:33.489 elif tag == 'equal':
2025-07-01 03:02:33.493 atags += ' ' * la
2025-07-01 03:02:33.502 btags += ' ' * lb
2025-07-01 03:02:33.510 else:
2025-07-01 03:02:33.521 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:33.531 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:33.537 else:
2025-07-01 03:02:33.550 # the synch pair is identical
2025-07-01 03:02:33.562 yield '  ' + aelt
2025-07-01 03:02:33.574
2025-07-01 03:02:33.582 # pump out diffs from after the synch point
2025-07-01 03:02:33.594 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:33.610
2025-07-01 03:02:33.622 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:33.630 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:33.642
2025-07-01 03:02:33.650 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:33.670 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:33.678 alo = 235, ahi = 1101
2025-07-01 03:02:33.690 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:33.706 blo = 235, bhi = 1101
2025-07-01 03:02:33.716
2025-07-01 03:02:33.726 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:33.735 g = []
2025-07-01 03:02:33.741 if alo < ahi:
2025-07-01 03:02:33.753 if blo < bhi:
2025-07-01 03:02:33.767 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:33.786 else:
2025-07-01 03:02:33.801 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:33.814 elif blo < bhi:
2025-07-01 03:02:33.830 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:33.842
2025-07-01 03:02:33.854 >       yield from g
2025-07-01 03:02:33.862
2025-07-01 03:02:33.874 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:33.882 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:33.890
2025-07-01 03:02:33.902 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:33.916 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:33.930 alo = 235, ahi = 1101
2025-07-01 03:02:33.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:33.952 blo = 235, bhi = 1101
2025-07-01 03:02:33.962
2025-07-01 03:02:33.976 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:33.986 r"""
2025-07-01 03:02:34.002 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:34.010 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:34.026 synch point, and intraline difference marking is done on the
2025-07-01 03:02:34.034 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:34.046
2025-07-01 03:02:34.062 Example:
2025-07-01 03:02:34.078
2025-07-01 03:02:34.086 >>> d = Differ()
2025-07-01 03:02:34.094 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:34.106 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:34.120 >>> print(''.join(results), end="")
2025-07-01 03:02:34.130 - abcDefghiJkl
2025-07-01 03:02:34.150 + abcdefGhijkl
2025-07-01 03:02:34.170 """
2025-07-01 03:02:34.182
2025-07-01 03:02:34.190 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:34.202 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:34.214 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:34.222 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:34.234 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:34.242
2025-07-01 03:02:34.256 # search for the pair that matches best without being identical
2025-07-01 03:02:34.269 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:34.279 # on junk -- unless we have to)
2025-07-01 03:02:34.294 for j in range(blo, bhi):
2025-07-01 03:02:34.306 bj = b[j]
2025-07-01 03:02:34.322 cruncher.set_seq2(bj)
2025-07-01 03:02:34.330 for i in range(alo, ahi):
2025-07-01 03:02:34.342 ai = a[i]
2025-07-01 03:02:34.354 if ai == bj:
2025-07-01 03:02:34.366 if eqi is None:
2025-07-01 03:02:34.378 eqi, eqj = i, j
2025-07-01 03:02:34.390 continue
2025-07-01 03:02:34.400 cruncher.set_seq1(ai)
2025-07-01 03:02:34.413 # computing similarity is expensive, so use the quick
2025-07-01 03:02:34.426 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:34.438 # compares by a factor of 3.
2025-07-01 03:02:34.450 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:34.458 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:34.471 # of the computation is cached by cruncher
2025-07-01 03:02:34.484 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:34.498 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:34.506 cruncher.ratio() > best_ratio:
2025-07-01 03:02:34.524 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:34.532 if best_ratio < cutoff:
2025-07-01 03:02:34.546 # no non-identical "pretty close" pair
2025-07-01 03:02:34.557 if eqi is None:
2025-07-01 03:02:34.566 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:34.582 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:34.590 return
2025-07-01 03:02:34.602 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:34.614 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:34.622 else:
2025-07-01 03:02:34.634 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:34.642 eqi = None
2025-07-01 03:02:34.654
2025-07-01 03:02:34.662 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:34.674 # identical
2025-07-01 03:02:34.688
2025-07-01 03:02:34.701 # pump out diffs from before the synch point
2025-07-01 03:02:34.709 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:34.722
2025-07-01 03:02:34.738 # do intraline marking on the synch pair
2025-07-01 03:02:34.750 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:34.762 if eqi is None:
2025-07-01 03:02:34.771 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:34.782 atags = btags = ""
2025-07-01 03:02:34.790 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:34.801 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:34.811 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:34.830 if tag == 'replace':
2025-07-01 03:02:34.845 atags += '^' * la
2025-07-01 03:02:34.858 btags += '^' * lb
2025-07-01 03:02:34.872 elif tag == 'delete':
2025-07-01 03:02:34.889 atags += '-' * la
2025-07-01 03:02:34.902 elif tag == 'insert':
2025-07-01 03:02:34.913 btags += '+' * lb
2025-07-01 03:02:34.925 elif tag == 'equal':
2025-07-01 03:02:34.938 atags += ' ' * la
2025-07-01 03:02:34.948 btags += ' ' * lb
2025-07-01 03:02:34.956 else:
2025-07-01 03:02:34.970 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:34.986 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:34.998 else:
2025-07-01 03:02:35.014 # the synch pair is identical
2025-07-01 03:02:35.022 yield '  ' + aelt
2025-07-01 03:02:35.034
2025-07-01 03:02:35.042 # pump out diffs from after the synch point
2025-07-01 03:02:35.058 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:35.066
2025-07-01 03:02:35.078 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:35.088 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:35.102
2025-07-01 03:02:35.122 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:35.137 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:35.150 alo = 236, ahi = 1101
2025-07-01 03:02:35.162 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:35.175 blo = 236, bhi = 1101
2025-07-01 03:02:35.190
2025-07-01 03:02:35.202 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:35.218 g = []
2025-07-01 03:02:35.226 if alo < ahi:
2025-07-01 03:02:35.239 if blo < bhi:
2025-07-01 03:02:35.254 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:35.265 else:
2025-07-01 03:02:35.278 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:35.290 elif blo < bhi:
2025-07-01 03:02:35.305 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:35.324
2025-07-01 03:02:35.334 >       yield from g
2025-07-01 03:02:35.342
2025-07-01 03:02:35.354 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:35.366 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:35.378
2025-07-01 03:02:35.387 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:35.394 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:35.406 alo = 236, ahi = 1101
2025-07-01 03:02:35.422 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:35.430 blo = 236, bhi = 1101
2025-07-01 03:02:35.438
2025-07-01 03:02:35.450 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:35.462 r"""
2025-07-01 03:02:35.478 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:35.494 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:35.502 synch point, and intraline difference marking is done on the
2025-07-01 03:02:35.517 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:35.530
2025-07-01 03:02:35.542 Example:
2025-07-01 03:02:35.554
2025-07-01 03:02:35.566 >>> d = Differ()
2025-07-01 03:02:35.582 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:35.598 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:35.614 >>> print(''.join(results), end="")
2025-07-01 03:02:35.626 - abcDefghiJkl
2025-07-01 03:02:35.646 + abcdefGhijkl
2025-07-01 03:02:35.666 """
2025-07-01 03:02:35.682
2025-07-01 03:02:35.708 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:35.730 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:35.746 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:35.754 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:35.771 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:35.790
2025-07-01 03:02:35.800 # search for the pair that matches best without being identical
2025-07-01 03:02:35.814 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:35.822 # on junk -- unless we have to)
2025-07-01 03:02:35.833 for j in range(blo, bhi):
2025-07-01 03:02:35.846 bj = b[j]
2025-07-01 03:02:35.858 cruncher.set_seq2(bj)
2025-07-01 03:02:35.873 for i in range(alo, ahi):
2025-07-01 03:02:35.882 ai = a[i]
2025-07-01 03:02:35.893 if ai == bj:
2025-07-01 03:02:35.906 if eqi is None:
2025-07-01 03:02:35.914 eqi, eqj = i, j
2025-07-01 03:02:35.922 continue
2025-07-01 03:02:35.934 cruncher.set_seq1(ai)
2025-07-01 03:02:35.946 # computing similarity is expensive, so use the quick
2025-07-01 03:02:35.962 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:35.974 # compares by a factor of 3.
2025-07-01 03:02:35.982 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:35.996 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:36.006 # of the computation is cached by cruncher
2025-07-01 03:02:36.018 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:36.030 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:36.038 cruncher.ratio() > best_ratio:
2025-07-01 03:02:36.054 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:36.070 if best_ratio < cutoff:
2025-07-01 03:02:36.082 # no non-identical "pretty close" pair
2025-07-01 03:02:36.094 if eqi is None:
2025-07-01 03:02:36.102 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:36.114 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:36.130 return
2025-07-01 03:02:36.142 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:36.154 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:36.162 else:
2025-07-01 03:02:36.174 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:36.182 eqi = None
2025-07-01 03:02:36.190
2025-07-01 03:02:36.202 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:36.210 # identical
2025-07-01 03:02:36.222
2025-07-01 03:02:36.232 # pump out diffs from before the synch point
2025-07-01 03:02:36.250 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:36.262
2025-07-01 03:02:36.274 # do intraline marking on the synch pair
2025-07-01 03:02:36.286 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:36.294 if eqi is None:
2025-07-01 03:02:36.306 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:36.318 atags = btags = ""
2025-07-01 03:02:36.330 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:36.338 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:36.354 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:36.362 if tag == 'replace':
2025-07-01 03:02:36.378 atags += '^' * la
2025-07-01 03:02:36.390 btags += '^' * lb
2025-07-01 03:02:36.407 elif tag == 'delete':
2025-07-01 03:02:36.418 atags += '-' * la
2025-07-01 03:02:36.425 elif tag == 'insert':
2025-07-01 03:02:36.442 btags += '+' * lb
2025-07-01 03:02:36.450 elif tag == 'equal':
2025-07-01 03:02:36.466 atags += ' ' * la
2025-07-01 03:02:36.482 btags += ' ' * lb
2025-07-01 03:02:36.490 else:
2025-07-01 03:02:36.497 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:36.515 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:36.526 else:
2025-07-01 03:02:36.535 # the synch pair is identical
2025-07-01 03:02:36.546 yield '  ' + aelt
2025-07-01 03:02:36.562
2025-07-01 03:02:36.574 # pump out diffs from after the synch point
2025-07-01 03:02:36.592 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:36.601
2025-07-01 03:02:36.617 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:36.630 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:36.646
2025-07-01 03:02:36.654 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:36.666 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:36.678 alo = 237, ahi = 1101
2025-07-01 03:02:36.694 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:36.710 blo = 237, bhi = 1101
2025-07-01 03:02:36.718
2025-07-01 03:02:36.730 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:36.738 g = []
2025-07-01 03:02:36.750 if alo < ahi:
2025-07-01 03:02:36.762 if blo < bhi:
2025-07-01 03:02:36.770 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:36.782 else:
2025-07-01 03:02:36.798 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:36.806 elif blo < bhi:
2025-07-01 03:02:36.818 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:36.830
2025-07-01 03:02:36.838 >       yield from g
2025-07-01 03:02:36.850
2025-07-01 03:02:36.862 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:36.870 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:36.882
2025-07-01 03:02:36.891 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:36.906 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:36.922 alo = 237, ahi = 1101
2025-07-01 03:02:36.938 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:36.953 blo = 237, bhi = 1101
2025-07-01 03:02:36.966
2025-07-01 03:02:36.982 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:36.994 r"""
2025-07-01 03:02:37.002 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:37.018 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:37.030 synch point, and intraline difference marking is done on the
2025-07-01 03:02:37.042 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:37.054
2025-07-01 03:02:37.066 Example:
2025-07-01 03:02:37.074
2025-07-01 03:02:37.082 >>> d = Differ()
2025-07-01 03:02:37.094 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:37.106 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:37.118 >>> print(''.join(results), end="")
2025-07-01 03:02:37.130 - abcDefghiJkl
2025-07-01 03:02:37.150 + abcdefGhijkl
2025-07-01 03:02:37.174 """
2025-07-01 03:02:37.192
2025-07-01 03:02:37.202 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:37.220 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:37.229 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:37.241 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:37.254 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:37.269
2025-07-01 03:02:37.281 # search for the pair that matches best without being identical
2025-07-01 03:02:37.297 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:37.309 # on junk -- unless we have to)
2025-07-01 03:02:37.321 for j in range(blo, bhi):
2025-07-01 03:02:37.330 bj = b[j]
2025-07-01 03:02:37.342 cruncher.set_seq2(bj)
2025-07-01 03:02:37.354 for i in range(alo, ahi):
2025-07-01 03:02:37.370 ai = a[i]
2025-07-01 03:02:37.382 if ai == bj:
2025-07-01 03:02:37.390 if eqi is None:
2025-07-01 03:02:37.405 eqi, eqj = i, j
2025-07-01 03:02:37.414 continue
2025-07-01 03:02:37.426 cruncher.set_seq1(ai)
2025-07-01 03:02:37.438 # computing similarity is expensive, so use the quick
2025-07-01 03:02:37.446 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:37.458 # compares by a factor of 3.
2025-07-01 03:02:37.473 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:37.486 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:37.494 # of the computation is cached by cruncher
2025-07-01 03:02:37.506 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:37.518 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:37.534 cruncher.ratio() > best_ratio:
2025-07-01 03:02:37.547 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:37.563 if best_ratio < cutoff:
2025-07-01 03:02:37.579 # no non-identical "pretty close" pair
2025-07-01 03:02:37.597 if eqi is None:
2025-07-01 03:02:37.611 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:37.623 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:37.645 return
2025-07-01 03:02:37.661 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:37.679 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:37.697 else:
2025-07-01 03:02:37.714 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:37.719 eqi = None
2025-07-01 03:02:37.724
2025-07-01 03:02:37.729 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:37.734 # identical
2025-07-01 03:02:37.739
2025-07-01 03:02:37.743 # pump out diffs from before the synch point
2025-07-01 03:02:37.748 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:37.752
2025-07-01 03:02:37.757 # do intraline marking on the synch pair
2025-07-01 03:02:37.761 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:37.766 if eqi is None:
2025-07-01 03:02:37.771 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:37.776 atags = btags = ""
2025-07-01 03:02:37.781 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:37.786 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:37.791 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:37.795 if tag == 'replace':
2025-07-01 03:02:37.800 atags += '^' * la
2025-07-01 03:02:37.805 btags += '^' * lb
2025-07-01 03:02:37.810 elif tag == 'delete':
2025-07-01 03:02:37.815 atags += '-' * la
2025-07-01 03:02:37.819 elif tag == 'insert':
2025-07-01 03:02:37.824 btags += '+' * lb
2025-07-01 03:02:37.829 elif tag == 'equal':
2025-07-01 03:02:37.835 atags += ' ' * la
2025-07-01 03:02:37.842 btags += ' ' * lb
2025-07-01 03:02:37.846 else:
2025-07-01 03:02:37.851 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:37.856 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:37.860 else:
2025-07-01 03:02:37.866 # the synch pair is identical
2025-07-01 03:02:37.871 yield '  ' + aelt
2025-07-01 03:02:37.875
2025-07-01 03:02:37.880 # pump out diffs from after the synch point
2025-07-01 03:02:37.885 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:37.889
2025-07-01 03:02:37.893 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:37.898 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:37.903
2025-07-01 03:02:37.907 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:37.913 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:37.917 alo = 238, ahi = 1101
2025-07-01 03:02:37.922 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:37.927 blo = 238, bhi = 1101
2025-07-01 03:02:37.931
2025-07-01 03:02:37.936 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:37.940 g = []
2025-07-01 03:02:37.944 if alo < ahi:
2025-07-01 03:02:37.949 if blo < bhi:
2025-07-01 03:02:37.953 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:37.958 else:
2025-07-01 03:02:37.962 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:37.966 elif blo < bhi:
2025-07-01 03:02:37.971 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:37.976
2025-07-01 03:02:37.980 >       yield from g
2025-07-01 03:02:37.984
2025-07-01 03:02:37.989 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:37.993 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:37.998
2025-07-01 03:02:38.003 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:38.009 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:38.015 alo = 238, ahi = 1101
2025-07-01 03:02:38.020 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:38.025 blo = 238, bhi = 1101
2025-07-01 03:02:38.030
2025-07-01 03:02:38.035 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:38.039 r"""
2025-07-01 03:02:38.044 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:38.049 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:38.054 synch point, and intraline difference marking is done on the
2025-07-01 03:02:38.058 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:38.062
2025-07-01 03:02:38.067 Example:
2025-07-01 03:02:38.071
2025-07-01 03:02:38.077 >>> d = Differ()
2025-07-01 03:02:38.082 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:38.088 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:38.093 >>> print(''.join(results), end="")
2025-07-01 03:02:38.098 - abcDefghiJkl
2025-07-01 03:02:38.110 + abcdefGhijkl
2025-07-01 03:02:38.120 """
2025-07-01 03:02:38.125
2025-07-01 03:02:38.130 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:38.136 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:38.141 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:38.146 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:38.152 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:38.156
2025-07-01 03:02:38.162 # search for the pair that matches best without being identical
2025-07-01 03:02:38.168 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:38.173 # on junk -- unless we have to)
2025-07-01 03:02:38.178 for j in range(blo, bhi):
2025-07-01 03:02:38.183 bj = b[j]
2025-07-01 03:02:38.194 cruncher.set_seq2(bj)
2025-07-01 03:02:38.200 for i in range(alo, ahi):
2025-07-01 03:02:38.206 ai = a[i]
2025-07-01 03:02:38.211 if ai == bj:
2025-07-01 03:02:38.216 if eqi is None:
2025-07-01 03:02:38.221 eqi, eqj = i, j
2025-07-01 03:02:38.225 continue
2025-07-01 03:02:38.229 cruncher.set_seq1(ai)
2025-07-01 03:02:38.234 # computing similarity is expensive, so use the quick
2025-07-01 03:02:38.239 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:38.243 # compares by a factor of 3.
2025-07-01 03:02:38.248 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:38.253 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:38.258 # of the computation is cached by cruncher
2025-07-01 03:02:38.263 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:38.267 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:38.272 cruncher.ratio() > best_ratio:
2025-07-01 03:02:38.276 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:38.281 if best_ratio < cutoff:
2025-07-01 03:02:38.286 # no non-identical "pretty close" pair
2025-07-01 03:02:38.290 if eqi is None:
2025-07-01 03:02:38.295 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:38.300 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:38.304 return
2025-07-01 03:02:38.309 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:38.314 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:38.319 else:
2025-07-01 03:02:38.324 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:38.328 eqi = None
2025-07-01 03:02:38.332
2025-07-01 03:02:38.337 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:38.341 # identical
2025-07-01 03:02:38.346
2025-07-01 03:02:38.350 # pump out diffs from before the synch point
2025-07-01 03:02:38.355 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:38.359
2025-07-01 03:02:38.364 # do intraline marking on the synch pair
2025-07-01 03:02:38.368 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:38.373 if eqi is None:
2025-07-01 03:02:38.377 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:38.383 atags = btags = ""
2025-07-01 03:02:38.387 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:38.393 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:38.397 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:38.402 if tag == 'replace':
2025-07-01 03:02:38.406 atags += '^' * la
2025-07-01 03:02:38.411 btags += '^' * lb
2025-07-01 03:02:38.416 elif tag == 'delete':
2025-07-01 03:02:38.420 atags += '-' * la
2025-07-01 03:02:38.425 elif tag == 'insert':
2025-07-01 03:02:38.429 btags += '+' * lb
2025-07-01 03:02:38.434 elif tag == 'equal':
2025-07-01 03:02:38.438 atags += ' ' * la
2025-07-01 03:02:38.443 btags += ' ' * lb
2025-07-01 03:02:38.447 else:
2025-07-01 03:02:38.453 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:38.459 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:38.465 else:
2025-07-01 03:02:38.472 # the synch pair is identical
2025-07-01 03:02:38.478 yield '  ' + aelt
2025-07-01 03:02:38.484
2025-07-01 03:02:38.490 # pump out diffs from after the synch point
2025-07-01 03:02:38.496 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:38.500
2025-07-01 03:02:38.505 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:38.510 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:38.515
2025-07-01 03:02:38.520 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:38.525 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:38.529 alo = 239, ahi = 1101
2025-07-01 03:02:38.535 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:38.540 blo = 239, bhi = 1101
2025-07-01 03:02:38.544
2025-07-01 03:02:38.549 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:38.553 g = []
2025-07-01 03:02:38.558 if alo < ahi:
2025-07-01 03:02:38.563 if blo < bhi:
2025-07-01 03:02:38.568 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:38.573 else:
2025-07-01 03:02:38.577 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:38.582 elif blo < bhi:
2025-07-01 03:02:38.586 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:38.591
2025-07-01 03:02:38.595 >       yield from g
2025-07-01 03:02:38.600
2025-07-01 03:02:38.604 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:38.609 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:38.613
2025-07-01 03:02:38.619 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:38.625 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:38.631 alo = 239, ahi = 1101
2025-07-01 03:02:38.637 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:38.643 blo = 239, bhi = 1101
2025-07-01 03:02:38.649
2025-07-01 03:02:38.659 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:38.665 r"""
2025-07-01 03:02:38.670 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:38.675 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:38.679 synch point, and intraline difference marking is done on the
2025-07-01 03:02:38.684 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:38.689
2025-07-01 03:02:38.694 Example:
2025-07-01 03:02:38.698
2025-07-01 03:02:38.703 >>> d = Differ()
2025-07-01 03:02:38.707 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:38.712 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:38.717 >>> print(''.join(results), end="")
2025-07-01 03:02:38.721 - abcDefghiJkl
2025-07-01 03:02:38.730 + abcdefGhijkl
2025-07-01 03:02:38.740 """
2025-07-01 03:02:38.745
2025-07-01 03:02:38.751 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:38.757 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:38.763 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:38.768 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:38.774 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:38.780
2025-07-01 03:02:38.786 # search for the pair that matches best without being identical
2025-07-01 03:02:38.792 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:38.797 # on junk -- unless we have to)
2025-07-01 03:02:38.803 for j in range(blo, bhi):
2025-07-01 03:02:38.809 bj = b[j]
2025-07-01 03:02:38.815 cruncher.set_seq2(bj)
2025-07-01 03:02:38.820 for i in range(alo, ahi):
2025-07-01 03:02:38.826 ai = a[i]
2025-07-01 03:02:38.832 if ai == bj:
2025-07-01 03:02:38.837 if eqi is None:
2025-07-01 03:02:38.843 eqi, eqj = i, j
2025-07-01 03:02:38.849 continue
2025-07-01 03:02:38.854 cruncher.set_seq1(ai)
2025-07-01 03:02:38.859 # computing similarity is expensive, so use the quick
2025-07-01 03:02:38.865 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:38.869 # compares by a factor of 3.
2025-07-01 03:02:38.874 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:38.878 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:38.883 # of the computation is cached by cruncher
2025-07-01 03:02:38.887 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:38.892 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:38.896 cruncher.ratio() > best_ratio:
2025-07-01 03:02:38.901 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:38.906 if best_ratio < cutoff:
2025-07-01 03:02:38.910 # no non-identical "pretty close" pair
2025-07-01 03:02:38.915 if eqi is None:
2025-07-01 03:02:38.920 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:38.925 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:38.929 return
2025-07-01 03:02:38.934 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:38.939 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:38.944 else:
2025-07-01 03:02:38.948 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:38.953 eqi = None
2025-07-01 03:02:38.958
2025-07-01 03:02:38.962 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:38.967 # identical
2025-07-01 03:02:38.971
2025-07-01 03:02:38.976 # pump out diffs from before the synch point
2025-07-01 03:02:38.981 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:38.985
2025-07-01 03:02:38.991 # do intraline marking on the synch pair
2025-07-01 03:02:38.996 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:39.000 if eqi is None:
2025-07-01 03:02:39.005 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:39.010 atags = btags = ""
2025-07-01 03:02:39.015 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:39.020 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:39.025 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:39.031 if tag == 'replace':
2025-07-01 03:02:39.036 atags += '^' * la
2025-07-01 03:02:39.041 btags += '^' * lb
2025-07-01 03:02:39.046 elif tag == 'delete':
2025-07-01 03:02:39.051 atags += '-' * la
2025-07-01 03:02:39.056 elif tag == 'insert':
2025-07-01 03:02:39.061 btags += '+' * lb
2025-07-01 03:02:39.066 elif tag == 'equal':
2025-07-01 03:02:39.071 atags += ' ' * la
2025-07-01 03:02:39.075 btags += ' ' * lb
2025-07-01 03:02:39.080 else:
2025-07-01 03:02:39.085 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:39.090 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:39.095 else:
2025-07-01 03:02:39.101 # the synch pair is identical
2025-07-01 03:02:39.106 yield '  ' + aelt
2025-07-01 03:02:39.111
2025-07-01 03:02:39.116 # pump out diffs from after the synch point
2025-07-01 03:02:39.121 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:39.126
2025-07-01 03:02:39.130 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:39.135 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:39.139
2025-07-01 03:02:39.144 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:39.149 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:39.153 alo = 240, ahi = 1101
2025-07-01 03:02:39.160 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:39.164 blo = 240, bhi = 1101
2025-07-01 03:02:39.169
2025-07-01 03:02:39.174 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:39.181 g = []
2025-07-01 03:02:39.187 if alo < ahi:
2025-07-01 03:02:39.192 if blo < bhi:
2025-07-01 03:02:39.198 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:39.205 else:
2025-07-01 03:02:39.210 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:39.214 elif blo < bhi:
2025-07-01 03:02:39.219 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:39.224
2025-07-01 03:02:39.229 >       yield from g
2025-07-01 03:02:39.234
2025-07-01 03:02:39.239 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:39.244 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:39.249
2025-07-01 03:02:39.253 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:39.259 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:39.263 alo = 240, ahi = 1101
2025-07-01 03:02:39.269 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:39.274 blo = 240, bhi = 1101
2025-07-01 03:02:39.279
2025-07-01 03:02:39.284 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:39.289 r"""
2025-07-01 03:02:39.294 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:39.299 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:39.304 synch point, and intraline difference marking is done on the
2025-07-01 03:02:39.309 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:39.314
2025-07-01 03:02:39.318 Example:
2025-07-01 03:02:39.323
2025-07-01 03:02:39.328 >>> d = Differ()
2025-07-01 03:02:39.333 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:39.338 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:39.342 >>> print(''.join(results), end="")
2025-07-01 03:02:39.348 - abcDefghiJkl
2025-07-01 03:02:39.357 + abcdefGhijkl
2025-07-01 03:02:39.366 """
2025-07-01 03:02:39.371
2025-07-01 03:02:39.376 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:39.381 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:39.385 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:39.390 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:39.395 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:39.400
2025-07-01 03:02:39.405 # search for the pair that matches best without being identical
2025-07-01 03:02:39.410 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:39.415 # on junk -- unless we have to)
2025-07-01 03:02:39.420 for j in range(blo, bhi):
2025-07-01 03:02:39.425 bj = b[j]
2025-07-01 03:02:39.429 cruncher.set_seq2(bj)
2025-07-01 03:02:39.435 for i in range(alo, ahi):
2025-07-01 03:02:39.440 ai = a[i]
2025-07-01 03:02:39.445 if ai == bj:
2025-07-01 03:02:39.450 if eqi is None:
2025-07-01 03:02:39.455 eqi, eqj = i, j
2025-07-01 03:02:39.460 continue
2025-07-01 03:02:39.465 cruncher.set_seq1(ai)
2025-07-01 03:02:39.470 # computing similarity is expensive, so use the quick
2025-07-01 03:02:39.475 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:39.480 # compares by a factor of 3.
2025-07-01 03:02:39.485 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:39.490 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:39.494 # of the computation is cached by cruncher
2025-07-01 03:02:39.499 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:39.504 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:39.508 cruncher.ratio() > best_ratio:
2025-07-01 03:02:39.513 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:39.518 if best_ratio < cutoff:
2025-07-01 03:02:39.522 # no non-identical "pretty close" pair
2025-07-01 03:02:39.527 if eqi is None:
2025-07-01 03:02:39.532 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:39.537 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:39.541 return
2025-07-01 03:02:39.545 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:39.550 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:39.554 else:
2025-07-01 03:02:39.559 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:39.563 eqi = None
2025-07-01 03:02:39.568
2025-07-01 03:02:39.573 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:39.577 # identical
2025-07-01 03:02:39.581
2025-07-01 03:02:39.586 # pump out diffs from before the synch point
2025-07-01 03:02:39.591 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:39.595
2025-07-01 03:02:39.600 # do intraline marking on the synch pair
2025-07-01 03:02:39.604 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:39.609 if eqi is None:
2025-07-01 03:02:39.613 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:39.618 atags = btags = ""
2025-07-01 03:02:39.622 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:39.627 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:39.631 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:39.635 if tag == 'replace':
2025-07-01 03:02:39.640 atags += '^' * la
2025-07-01 03:02:39.644 btags += '^' * lb
2025-07-01 03:02:39.649 elif tag == 'delete':
2025-07-01 03:02:39.653 atags += '-' * la
2025-07-01 03:02:39.658 elif tag == 'insert':
2025-07-01 03:02:39.663 btags += '+' * lb
2025-07-01 03:02:39.667 elif tag == 'equal':
2025-07-01 03:02:39.672 atags += ' ' * la
2025-07-01 03:02:39.676 btags += ' ' * lb
2025-07-01 03:02:39.681 else:
2025-07-01 03:02:39.686 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:39.690 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:39.694 else:
2025-07-01 03:02:39.699 # the synch pair is identical
2025-07-01 03:02:39.703 yield '  ' + aelt
2025-07-01 03:02:39.708
2025-07-01 03:02:39.712 # pump out diffs from after the synch point
2025-07-01 03:02:39.716 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:39.721
2025-07-01 03:02:39.725 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:39.730 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:39.734
2025-07-01 03:02:39.738 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:39.743 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:39.747 alo = 241, ahi = 1101
2025-07-01 03:02:39.752 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:39.757 blo = 241, bhi = 1101
2025-07-01 03:02:39.761
2025-07-01 03:02:39.766 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:39.770 g = []
2025-07-01 03:02:39.774 if alo < ahi:
2025-07-01 03:02:39.779 if blo < bhi:
2025-07-01 03:02:39.783 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:39.787 else:
2025-07-01 03:02:39.792 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:39.796 elif blo < bhi:
2025-07-01 03:02:39.801 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:39.805
2025-07-01 03:02:39.810 >       yield from g
2025-07-01 03:02:39.814
2025-07-01 03:02:39.818 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:39.823 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:39.828
2025-07-01 03:02:39.832 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:39.837 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:39.841 alo = 241, ahi = 1101
2025-07-01 03:02:39.846 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:39.851 blo = 241, bhi = 1101
2025-07-01 03:02:39.855
2025-07-01 03:02:39.859 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:39.864 r"""
2025-07-01 03:02:39.868 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:39.873 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:39.877 synch point, and intraline difference marking is done on the
2025-07-01 03:02:39.881 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:39.885
2025-07-01 03:02:39.890 Example:
2025-07-01 03:02:39.894
2025-07-01 03:02:39.898 >>> d = Differ()
2025-07-01 03:02:39.903 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:39.907 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:39.911 >>> print(''.join(results), end="")
2025-07-01 03:02:39.916 - abcDefghiJkl
2025-07-01 03:02:39.925 + abcdefGhijkl
2025-07-01 03:02:39.934 """
2025-07-01 03:02:39.938
2025-07-01 03:02:39.943 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:39.948 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:39.952 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:39.957 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:39.962 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:39.966
2025-07-01 03:02:39.971 # search for the pair that matches best without being identical
2025-07-01 03:02:39.975 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:39.979 # on junk -- unless we have to)
2025-07-01 03:02:39.984 for j in range(blo, bhi):
2025-07-01 03:02:39.988 bj = b[j]
2025-07-01 03:02:39.993 cruncher.set_seq2(bj)
2025-07-01 03:02:39.997 for i in range(alo, ahi):
2025-07-01 03:02:40.002 ai = a[i]
2025-07-01 03:02:40.006 if ai == bj:
2025-07-01 03:02:40.011 if eqi is None:
2025-07-01 03:02:40.015 eqi, eqj = i, j
2025-07-01 03:02:40.020 continue
2025-07-01 03:02:40.024 cruncher.set_seq1(ai)
2025-07-01 03:02:40.030 # computing similarity is expensive, so use the quick
2025-07-01 03:02:40.034 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:40.039 # compares by a factor of 3.
2025-07-01 03:02:40.044 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:40.048 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:40.053 # of the computation is cached by cruncher
2025-07-01 03:02:40.057 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:40.062 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:40.066 cruncher.ratio() > best_ratio:
2025-07-01 03:02:40.071 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:40.075 if best_ratio < cutoff:
2025-07-01 03:02:40.080 # no non-identical "pretty close" pair
2025-07-01 03:02:40.084 if eqi is None:
2025-07-01 03:02:40.089 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:40.093 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:40.097 return
2025-07-01 03:02:40.102 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:40.107 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:40.111 else:
2025-07-01 03:02:40.116 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:40.120 eqi = None
2025-07-01 03:02:40.124
2025-07-01 03:02:40.129 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:40.133 # identical
2025-07-01 03:02:40.137
2025-07-01 03:02:40.142 # pump out diffs from before the synch point
2025-07-01 03:02:40.146 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:40.150
2025-07-01 03:02:40.154 # do intraline marking on the synch pair
2025-07-01 03:02:40.159 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:40.163 if eqi is None:
2025-07-01 03:02:40.167 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:40.172 atags = btags = ""
2025-07-01 03:02:40.176 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:40.181 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:40.185 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:40.189 if tag == 'replace':
2025-07-01 03:02:40.194 atags += '^' * la
2025-07-01 03:02:40.198 btags += '^' * lb
2025-07-01 03:02:40.203 elif tag == 'delete':
2025-07-01 03:02:40.207 atags += '-' * la
2025-07-01 03:02:40.211 elif tag == 'insert':
2025-07-01 03:02:40.216 btags += '+' * lb
2025-07-01 03:02:40.220 elif tag == 'equal':
2025-07-01 03:02:40.225 atags += ' ' * la
2025-07-01 03:02:40.229 btags += ' ' * lb
2025-07-01 03:02:40.233 else:
2025-07-01 03:02:40.238 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:40.242 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:40.246 else:
2025-07-01 03:02:40.251 # the synch pair is identical
2025-07-01 03:02:40.256 yield '  ' + aelt
2025-07-01 03:02:40.261
2025-07-01 03:02:40.265 # pump out diffs from after the synch point
2025-07-01 03:02:40.270 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:40.274
2025-07-01 03:02:40.278 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:40.283 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:40.287
2025-07-01 03:02:40.292 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:40.296 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:40.301 alo = 242, ahi = 1101
2025-07-01 03:02:40.305 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:40.310 blo = 242, bhi = 1101
2025-07-01 03:02:40.314
2025-07-01 03:02:40.318 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:40.322 g = []
2025-07-01 03:02:40.327 if alo < ahi:
2025-07-01 03:02:40.331 if blo < bhi:
2025-07-01 03:02:40.335 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:40.340 else:
2025-07-01 03:02:40.344 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:40.348 elif blo < bhi:
2025-07-01 03:02:40.352 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:40.357
2025-07-01 03:02:40.361 >       yield from g
2025-07-01 03:02:40.365
2025-07-01 03:02:40.369 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:40.374 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:40.379
2025-07-01 03:02:40.385 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:40.392 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:40.398 alo = 242, ahi = 1101
2025-07-01 03:02:40.403 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:40.407 blo = 242, bhi = 1101
2025-07-01 03:02:40.411
2025-07-01 03:02:40.416 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:40.420 r"""
2025-07-01 03:02:40.425 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:40.429 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:40.433 synch point, and intraline difference marking is done on the
2025-07-01 03:02:40.438 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:40.442
2025-07-01 03:02:40.447 Example:
2025-07-01 03:02:40.451
2025-07-01 03:02:40.455 >>> d = Differ()
2025-07-01 03:02:40.459 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:40.464 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:40.468 >>> print(''.join(results), end="")
2025-07-01 03:02:40.472 - abcDefghiJkl
2025-07-01 03:02:40.481 + abcdefGhijkl
2025-07-01 03:02:40.490 """
2025-07-01 03:02:40.494
2025-07-01 03:02:40.498 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:40.502 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:40.507 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:40.511 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:40.516 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:40.520
2025-07-01 03:02:40.524 # search for the pair that matches best without being identical
2025-07-01 03:02:40.529 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:40.533 # on junk -- unless we have to)
2025-07-01 03:02:40.537 for j in range(blo, bhi):
2025-07-01 03:02:40.542 bj = b[j]
2025-07-01 03:02:40.546 cruncher.set_seq2(bj)
2025-07-01 03:02:40.550 for i in range(alo, ahi):
2025-07-01 03:02:40.555 ai = a[i]
2025-07-01 03:02:40.559 if ai == bj:
2025-07-01 03:02:40.563 if eqi is None:
2025-07-01 03:02:40.568 eqi, eqj = i, j
2025-07-01 03:02:40.572 continue
2025-07-01 03:02:40.577 cruncher.set_seq1(ai)
2025-07-01 03:02:40.581 # computing similarity is expensive, so use the quick
2025-07-01 03:02:40.585 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:40.590 # compares by a factor of 3.
2025-07-01 03:02:40.595 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:40.599 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:40.604 # of the computation is cached by cruncher
2025-07-01 03:02:40.608 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:40.613 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:40.617 cruncher.ratio() > best_ratio:
2025-07-01 03:02:40.622 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:40.626 if best_ratio < cutoff:
2025-07-01 03:02:40.631 # no non-identical "pretty close" pair
2025-07-01 03:02:40.635 if eqi is None:
2025-07-01 03:02:40.639 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:40.644 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:40.648 return
2025-07-01 03:02:40.653 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:40.657 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:40.662 else:
2025-07-01 03:02:40.667 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:40.671 eqi = None
2025-07-01 03:02:40.676
2025-07-01 03:02:40.680 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:40.685 # identical
2025-07-01 03:02:40.689
2025-07-01 03:02:40.693 # pump out diffs from before the synch point
2025-07-01 03:02:40.698 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:40.702
2025-07-01 03:02:40.707 # do intraline marking on the synch pair
2025-07-01 03:02:40.711 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:40.715 if eqi is None:
2025-07-01 03:02:40.720 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:40.724 atags = btags = ""
2025-07-01 03:02:40.729 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:40.733 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:40.738 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:40.743 if tag == 'replace':
2025-07-01 03:02:40.747 atags += '^' * la
2025-07-01 03:02:40.751 btags += '^' * lb
2025-07-01 03:02:40.756 elif tag == 'delete':
2025-07-01 03:02:40.760 atags += '-' * la
2025-07-01 03:02:40.765 elif tag == 'insert':
2025-07-01 03:02:40.769 btags += '+' * lb
2025-07-01 03:02:40.774 elif tag == 'equal':
2025-07-01 03:02:40.778 atags += ' ' * la
2025-07-01 03:02:40.782 btags += ' ' * lb
2025-07-01 03:02:40.787 else:
2025-07-01 03:02:40.791 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:40.796 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:40.800 else:
2025-07-01 03:02:40.804 # the synch pair is identical
2025-07-01 03:02:40.808 yield '  ' + aelt
2025-07-01 03:02:40.813
2025-07-01 03:02:40.818 # pump out diffs from after the synch point
2025-07-01 03:02:40.822 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:40.826
2025-07-01 03:02:40.831 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:40.835 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:40.840
2025-07-01 03:02:40.844 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:40.849 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:40.854 alo = 243, ahi = 1101
2025-07-01 03:02:40.859 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:40.864 blo = 243, bhi = 1101
2025-07-01 03:02:40.868
2025-07-01 03:02:40.873 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:40.878 g = []
2025-07-01 03:02:40.882 if alo < ahi:
2025-07-01 03:02:40.887 if blo < bhi:
2025-07-01 03:02:40.891 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:40.896 else:
2025-07-01 03:02:40.900 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:40.905 elif blo < bhi:
2025-07-01 03:02:40.909 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:40.914
2025-07-01 03:02:40.918 >       yield from g
2025-07-01 03:02:40.922
2025-07-01 03:02:40.927 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:40.931 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:40.935
2025-07-01 03:02:40.940 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:40.945 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:40.949 alo = 243, ahi = 1101
2025-07-01 03:02:40.954 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:40.958 blo = 243, bhi = 1101
2025-07-01 03:02:40.963
2025-07-01 03:02:40.967 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:40.972 r"""
2025-07-01 03:02:40.976 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:40.981 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:40.985 synch point, and intraline difference marking is done on the
2025-07-01 03:02:40.990 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:40.994
2025-07-01 03:02:40.999 Example:
2025-07-01 03:02:41.004
2025-07-01 03:02:41.008 >>> d = Differ()
2025-07-01 03:02:41.013 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:41.017 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:41.022 >>> print(''.join(results), end="")
2025-07-01 03:02:41.026 - abcDefghiJkl
2025-07-01 03:02:41.036 + abcdefGhijkl
2025-07-01 03:02:41.045 """
2025-07-01 03:02:41.049
2025-07-01 03:02:41.053 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:41.058 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:41.062 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:41.067 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:41.071 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:41.075
2025-07-01 03:02:41.080 # search for the pair that matches best without being identical
2025-07-01 03:02:41.084 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:41.088 # on junk -- unless we have to)
2025-07-01 03:02:41.093 for j in range(blo, bhi):
2025-07-01 03:02:41.097 bj = b[j]
2025-07-01 03:02:41.102 cruncher.set_seq2(bj)
2025-07-01 03:02:41.106 for i in range(alo, ahi):
2025-07-01 03:02:41.110 ai = a[i]
2025-07-01 03:02:41.115 if ai == bj:
2025-07-01 03:02:41.119 if eqi is None:
2025-07-01 03:02:41.123 eqi, eqj = i, j
2025-07-01 03:02:41.127 continue
2025-07-01 03:02:41.132 cruncher.set_seq1(ai)
2025-07-01 03:02:41.136 # computing similarity is expensive, so use the quick
2025-07-01 03:02:41.141 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:41.145 # compares by a factor of 3.
2025-07-01 03:02:41.149 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:41.154 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:41.158 # of the computation is cached by cruncher
2025-07-01 03:02:41.162 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:41.167 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:41.172 cruncher.ratio() > best_ratio:
2025-07-01 03:02:41.176 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:41.181 if best_ratio < cutoff:
2025-07-01 03:02:41.185 # no non-identical "pretty close" pair
2025-07-01 03:02:41.190 if eqi is None:
2025-07-01 03:02:41.194 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:41.199 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:41.203 return
2025-07-01 03:02:41.207 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:41.212 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:41.216 else:
2025-07-01 03:02:41.221 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:41.225 eqi = None
2025-07-01 03:02:41.229
2025-07-01 03:02:41.234 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:41.238 # identical
2025-07-01 03:02:41.242
2025-07-01 03:02:41.247 # pump out diffs from before the synch point
2025-07-01 03:02:41.251 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:41.256
2025-07-01 03:02:41.260 # do intraline marking on the synch pair
2025-07-01 03:02:41.265 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:41.269 if eqi is None:
2025-07-01 03:02:41.274 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:41.279 atags = btags = ""
2025-07-01 03:02:41.286 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:41.292 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:41.296 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:41.301 if tag == 'replace':
2025-07-01 03:02:41.305 atags += '^' * la
2025-07-01 03:02:41.310 btags += '^' * lb
2025-07-01 03:02:41.316 elif tag == 'delete':
2025-07-01 03:02:41.322 atags += '-' * la
2025-07-01 03:02:41.328 elif tag == 'insert':
2025-07-01 03:02:41.333 btags += '+' * lb
2025-07-01 03:02:41.338 elif tag == 'equal':
2025-07-01 03:02:41.342 atags += ' ' * la
2025-07-01 03:02:41.346 btags += ' ' * lb
2025-07-01 03:02:41.352 else:
2025-07-01 03:02:41.358 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:41.365 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:41.372 else:
2025-07-01 03:02:41.379 # the synch pair is identical
2025-07-01 03:02:41.385 yield '  ' + aelt
2025-07-01 03:02:41.391
2025-07-01 03:02:41.397 # pump out diffs from after the synch point
2025-07-01 03:02:41.403 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:41.407
2025-07-01 03:02:41.412 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:41.416 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:41.420
2025-07-01 03:02:41.425 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:41.430 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:41.434 alo = 246, ahi = 1101
2025-07-01 03:02:41.439 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:41.444 blo = 246, bhi = 1101
2025-07-01 03:02:41.449
2025-07-01 03:02:41.453 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:41.458 g = []
2025-07-01 03:02:41.462 if alo < ahi:
2025-07-01 03:02:41.466 if blo < bhi:
2025-07-01 03:02:41.471 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:41.475 else:
2025-07-01 03:02:41.480 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:41.484 elif blo < bhi:
2025-07-01 03:02:41.489 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:41.493
2025-07-01 03:02:41.498 >       yield from g
2025-07-01 03:02:41.502
2025-07-01 03:02:41.506 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:41.511 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:41.515
2025-07-01 03:02:41.520 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:41.524 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:41.529 alo = 246, ahi = 1101
2025-07-01 03:02:41.534 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:41.538 blo = 246, bhi = 1101
2025-07-01 03:02:41.542
2025-07-01 03:02:41.547 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:41.552 r"""
2025-07-01 03:02:41.556 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:41.561 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:41.566 synch point, and intraline difference marking is done on the
2025-07-01 03:02:41.570 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:41.575
2025-07-01 03:02:41.580 Example:
2025-07-01 03:02:41.585
2025-07-01 03:02:41.589 >>> d = Differ()
2025-07-01 03:02:41.594 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:41.599 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:41.603 >>> print(''.join(results), end="")
2025-07-01 03:02:41.608 - abcDefghiJkl
2025-07-01 03:02:41.618 + abcdefGhijkl
2025-07-01 03:02:41.626 """
2025-07-01 03:02:41.631
2025-07-01 03:02:41.635 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:41.639 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:41.644 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:41.648 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:41.653 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:41.657
2025-07-01 03:02:41.662 # search for the pair that matches best without being identical
2025-07-01 03:02:41.667 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:41.671 # on junk -- unless we have to)
2025-07-01 03:02:41.676 for j in range(blo, bhi):
2025-07-01 03:02:41.680 bj = b[j]
2025-07-01 03:02:41.685 cruncher.set_seq2(bj)
2025-07-01 03:02:41.689 for i in range(alo, ahi):
2025-07-01 03:02:41.694 ai = a[i]
2025-07-01 03:02:41.698 if ai == bj:
2025-07-01 03:02:41.703 if eqi is None:
2025-07-01 03:02:41.708 eqi, eqj = i, j
2025-07-01 03:02:41.712 continue
2025-07-01 03:02:41.716 cruncher.set_seq1(ai)
2025-07-01 03:02:41.721 # computing similarity is expensive, so use the quick
2025-07-01 03:02:41.725 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:41.729 # compares by a factor of 3.
2025-07-01 03:02:41.734 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:41.738 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:41.743 # of the computation is cached by cruncher
2025-07-01 03:02:41.747 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:41.751 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:41.756 cruncher.ratio() > best_ratio:
2025-07-01 03:02:41.760 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:41.765 if best_ratio < cutoff:
2025-07-01 03:02:41.769 # no non-identical "pretty close" pair
2025-07-01 03:02:41.774 if eqi is None:
2025-07-01 03:02:41.778 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:41.782 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:41.787 return
2025-07-01 03:02:41.791 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:41.795 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:41.800 else:
2025-07-01 03:02:41.805 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:41.809 eqi = None
2025-07-01 03:02:41.813
2025-07-01 03:02:41.818 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:41.822 # identical
2025-07-01 03:02:41.826
2025-07-01 03:02:41.831 # pump out diffs from before the synch point
2025-07-01 03:02:41.835 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:41.839
2025-07-01 03:02:41.843 # do intraline marking on the synch pair
2025-07-01 03:02:41.848 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:41.852 if eqi is None:
2025-07-01 03:02:41.857 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:41.862 atags = btags = ""
2025-07-01 03:02:41.866 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:41.871 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:41.875 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:41.880 if tag == 'replace':
2025-07-01 03:02:41.884 atags += '^' * la
2025-07-01 03:02:41.888 btags += '^' * lb
2025-07-01 03:02:41.893 elif tag == 'delete':
2025-07-01 03:02:41.897 atags += '-' * la
2025-07-01 03:02:41.901 elif tag == 'insert':
2025-07-01 03:02:41.906 btags += '+' * lb
2025-07-01 03:02:41.910 elif tag == 'equal':
2025-07-01 03:02:41.915 atags += ' ' * la
2025-07-01 03:02:41.919 btags += ' ' * lb
2025-07-01 03:02:41.924 else:
2025-07-01 03:02:41.928 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:41.933 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:41.937 else:
2025-07-01 03:02:41.941 # the synch pair is identical
2025-07-01 03:02:41.946 yield '  ' + aelt
2025-07-01 03:02:41.950
2025-07-01 03:02:41.954 # pump out diffs from after the synch point
2025-07-01 03:02:41.959 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:41.963
2025-07-01 03:02:41.967 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:41.972 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:41.977
2025-07-01 03:02:41.981 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:41.986 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:41.990 alo = 247, ahi = 1101
2025-07-01 03:02:41.995 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:42.000 blo = 247, bhi = 1101
2025-07-01 03:02:42.004
2025-07-01 03:02:42.009 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:42.014 g = []
2025-07-01 03:02:42.019 if alo < ahi:
2025-07-01 03:02:42.023 if blo < bhi:
2025-07-01 03:02:42.028 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:42.032 else:
2025-07-01 03:02:42.037 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:42.042 elif blo < bhi:
2025-07-01 03:02:42.046 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:42.050
2025-07-01 03:02:42.055 >       yield from g
2025-07-01 03:02:42.059
2025-07-01 03:02:42.063 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:42.068 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:42.072
2025-07-01 03:02:42.077 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:42.082 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:42.086 alo = 247, ahi = 1101
2025-07-01 03:02:42.091 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:42.095 blo = 247, bhi = 1101
2025-07-01 03:02:42.100
2025-07-01 03:02:42.104 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:42.109 r"""
2025-07-01 03:02:42.114 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:42.118 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:42.123 synch point, and intraline difference marking is done on the
2025-07-01 03:02:42.127 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:42.131
2025-07-01 03:02:42.136 Example:
2025-07-01 03:02:42.141
2025-07-01 03:02:42.145 >>> d = Differ()
2025-07-01 03:02:42.149 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:42.154 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:42.158 >>> print(''.join(results), end="")
2025-07-01 03:02:42.162 - abcDefghiJkl
2025-07-01 03:02:42.171 + abcdefGhijkl
2025-07-01 03:02:42.180 """
2025-07-01 03:02:42.184
2025-07-01 03:02:42.189 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:42.194 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:42.198 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:42.203 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:42.207 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:42.211
2025-07-01 03:02:42.216 # search for the pair that matches best without being identical
2025-07-01 03:02:42.221 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:42.225 # on junk -- unless we have to)
2025-07-01 03:02:42.230 for j in range(blo, bhi):
2025-07-01 03:02:42.235 bj = b[j]
2025-07-01 03:02:42.240 cruncher.set_seq2(bj)
2025-07-01 03:02:42.244 for i in range(alo, ahi):
2025-07-01 03:02:42.248 ai = a[i]
2025-07-01 03:02:42.253 if ai == bj:
2025-07-01 03:02:42.257 if eqi is None:
2025-07-01 03:02:42.262 eqi, eqj = i, j
2025-07-01 03:02:42.266 continue
2025-07-01 03:02:42.270 cruncher.set_seq1(ai)
2025-07-01 03:02:42.275 # computing similarity is expensive, so use the quick
2025-07-01 03:02:42.279 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:42.284 # compares by a factor of 3.
2025-07-01 03:02:42.288 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:42.293 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:42.298 # of the computation is cached by cruncher
2025-07-01 03:02:42.305 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:42.310 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:42.314 cruncher.ratio() > best_ratio:
2025-07-01 03:02:42.319 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:42.324 if best_ratio < cutoff:
2025-07-01 03:02:42.329 # no non-identical "pretty close" pair
2025-07-01 03:02:42.333 if eqi is None:
2025-07-01 03:02:42.337 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:42.342 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:42.346 return
2025-07-01 03:02:42.351 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:42.356 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:42.360 else:
2025-07-01 03:02:42.364 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:42.369 eqi = None
2025-07-01 03:02:42.373
2025-07-01 03:02:42.378 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:42.382 # identical
2025-07-01 03:02:42.387
2025-07-01 03:02:42.391 # pump out diffs from before the synch point
2025-07-01 03:02:42.396 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:42.401
2025-07-01 03:02:42.405 # do intraline marking on the synch pair
2025-07-01 03:02:42.409 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:42.415 if eqi is None:
2025-07-01 03:02:42.419 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:42.424 atags = btags = ""
2025-07-01 03:02:42.428 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:42.433 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:42.437 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:42.442 if tag == 'replace':
2025-07-01 03:02:42.446 atags += '^' * la
2025-07-01 03:02:42.451 btags += '^' * lb
2025-07-01 03:02:42.455 elif tag == 'delete':
2025-07-01 03:02:42.459 atags += '-' * la
2025-07-01 03:02:42.464 elif tag == 'insert':
2025-07-01 03:02:42.468 btags += '+' * lb
2025-07-01 03:02:42.473 elif tag == 'equal':
2025-07-01 03:02:42.477 atags += ' ' * la
2025-07-01 03:02:42.481 btags += ' ' * lb
2025-07-01 03:02:42.486 else:
2025-07-01 03:02:42.490 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:42.495 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:42.499 else:
2025-07-01 03:02:42.504 # the synch pair is identical
2025-07-01 03:02:42.508 yield '  ' + aelt
2025-07-01 03:02:42.513
2025-07-01 03:02:42.517 # pump out diffs from after the synch point
2025-07-01 03:02:42.522 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:42.526
2025-07-01 03:02:42.530 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:42.535 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:42.540
2025-07-01 03:02:42.544 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:42.549 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:42.553 alo = 248, ahi = 1101
2025-07-01 03:02:42.558 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:42.562 blo = 248, bhi = 1101
2025-07-01 03:02:42.567
2025-07-01 03:02:42.571 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:42.575 g = []
2025-07-01 03:02:42.580 if alo < ahi:
2025-07-01 03:02:42.584 if blo < bhi:
2025-07-01 03:02:42.589 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:42.593 else:
2025-07-01 03:02:42.598 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:42.602 elif blo < bhi:
2025-07-01 03:02:42.607 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:42.612
2025-07-01 03:02:42.618 >       yield from g
2025-07-01 03:02:42.625
2025-07-01 03:02:42.630 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:42.637 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:42.642
2025-07-01 03:02:42.648 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:42.654 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:42.660 alo = 248, ahi = 1101
2025-07-01 03:02:42.666 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:42.671 blo = 248, bhi = 1101
2025-07-01 03:02:42.676
2025-07-01 03:02:42.681 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:42.685 r"""
2025-07-01 03:02:42.690 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:42.694 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:42.699 synch point, and intraline difference marking is done on the
2025-07-01 03:02:42.703 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:42.708
2025-07-01 03:02:42.712 Example:
2025-07-01 03:02:42.717
2025-07-01 03:02:42.721 >>> d = Differ()
2025-07-01 03:02:42.726 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:42.732 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:42.737 >>> print(''.join(results), end="")
2025-07-01 03:02:42.741 - abcDefghiJkl
2025-07-01 03:02:42.750 + abcdefGhijkl
2025-07-01 03:02:42.759 """
2025-07-01 03:02:42.763
2025-07-01 03:02:42.767 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:42.772 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:42.776 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:42.780 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:42.785 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:42.789
2025-07-01 03:02:42.793 # search for the pair that matches best without being identical
2025-07-01 03:02:42.798 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:42.802 # on junk -- unless we have to)
2025-07-01 03:02:42.807 for j in range(blo, bhi):
2025-07-01 03:02:42.811 bj = b[j]
2025-07-01 03:02:42.816 cruncher.set_seq2(bj)
2025-07-01 03:02:42.821 for i in range(alo, ahi):
2025-07-01 03:02:42.825 ai = a[i]
2025-07-01 03:02:42.830 if ai == bj:
2025-07-01 03:02:42.835 if eqi is None:
2025-07-01 03:02:42.839 eqi, eqj = i, j
2025-07-01 03:02:42.844 continue
2025-07-01 03:02:42.848 cruncher.set_seq1(ai)
2025-07-01 03:02:42.853 # computing similarity is expensive, so use the quick
2025-07-01 03:02:42.858 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:42.863 # compares by a factor of 3.
2025-07-01 03:02:42.867 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:42.872 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:42.879 # of the computation is cached by cruncher
2025-07-01 03:02:42.883 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:42.888 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:42.892 cruncher.ratio() > best_ratio:
2025-07-01 03:02:42.897 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:42.901 if best_ratio < cutoff:
2025-07-01 03:02:42.906 # no non-identical "pretty close" pair
2025-07-01 03:02:42.910 if eqi is None:
2025-07-01 03:02:42.915 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:42.920 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:42.924 return
2025-07-01 03:02:42.929 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:42.933 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:42.938 else:
2025-07-01 03:02:42.942 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:42.946 eqi = None
2025-07-01 03:02:42.951
2025-07-01 03:02:42.955 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:42.959 # identical
2025-07-01 03:02:42.964
2025-07-01 03:02:42.968 # pump out diffs from before the synch point
2025-07-01 03:02:42.972 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:42.976
2025-07-01 03:02:42.981 # do intraline marking on the synch pair
2025-07-01 03:02:42.985 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:42.989 if eqi is None:
2025-07-01 03:02:42.994 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:42.998 atags = btags = ""
2025-07-01 03:02:43.003 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:43.009 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:43.015 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:43.022 if tag == 'replace':
2025-07-01 03:02:43.028 atags += '^' * la
2025-07-01 03:02:43.034 btags += '^' * lb
2025-07-01 03:02:43.040 elif tag == 'delete':
2025-07-01 03:02:43.046 atags += '-' * la
2025-07-01 03:02:43.053 elif tag == 'insert':
2025-07-01 03:02:43.059 btags += '+' * lb
2025-07-01 03:02:43.065 elif tag == 'equal':
2025-07-01 03:02:43.071 atags += ' ' * la
2025-07-01 03:02:43.077 btags += ' ' * lb
2025-07-01 03:02:43.083 else:
2025-07-01 03:02:43.089 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:43.095 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:43.101 else:
2025-07-01 03:02:43.107 # the synch pair is identical
2025-07-01 03:02:43.113 yield '  ' + aelt
2025-07-01 03:02:43.118
2025-07-01 03:02:43.123 # pump out diffs from after the synch point
2025-07-01 03:02:43.127 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:43.132
2025-07-01 03:02:43.137 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:43.142 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:43.146
2025-07-01 03:02:43.151 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:43.156 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:43.160 alo = 249, ahi = 1101
2025-07-01 03:02:43.165 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:43.169 blo = 249, bhi = 1101
2025-07-01 03:02:43.174
2025-07-01 03:02:43.178 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:43.182 g = []
2025-07-01 03:02:43.187 if alo < ahi:
2025-07-01 03:02:43.191 if blo < bhi:
2025-07-01 03:02:43.196 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:43.200 else:
2025-07-01 03:02:43.205 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:43.209 elif blo < bhi:
2025-07-01 03:02:43.213 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:43.218
2025-07-01 03:02:43.222 >       yield from g
2025-07-01 03:02:43.226
2025-07-01 03:02:43.230 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:43.235 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:43.239
2025-07-01 03:02:43.244 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:43.250 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:43.254 alo = 249, ahi = 1101
2025-07-01 03:02:43.259 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:43.263 blo = 249, bhi = 1101
2025-07-01 03:02:43.267
2025-07-01 03:02:43.272 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:43.276 r"""
2025-07-01 03:02:43.280 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:43.285 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:43.289 synch point, and intraline difference marking is done on the
2025-07-01 03:02:43.294 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:43.298
2025-07-01 03:02:43.302 Example:
2025-07-01 03:02:43.306
2025-07-01 03:02:43.310 >>> d = Differ()
2025-07-01 03:02:43.315 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:43.319 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:43.324 >>> print(''.join(results), end="")
2025-07-01 03:02:43.330 - abcDefghiJkl
2025-07-01 03:02:43.339 + abcdefGhijkl
2025-07-01 03:02:43.347 """
2025-07-01 03:02:43.351
2025-07-01 03:02:43.356 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:43.361 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:43.365 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:43.370 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:43.374 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:43.379
2025-07-01 03:02:43.383 # search for the pair that matches best without being identical
2025-07-01 03:02:43.388 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:43.392 # on junk -- unless we have to)
2025-07-01 03:02:43.397 for j in range(blo, bhi):
2025-07-01 03:02:43.401 bj = b[j]
2025-07-01 03:02:43.406 cruncher.set_seq2(bj)
2025-07-01 03:02:43.410 for i in range(alo, ahi):
2025-07-01 03:02:43.415 ai = a[i]
2025-07-01 03:02:43.419 if ai == bj:
2025-07-01 03:02:43.423 if eqi is None:
2025-07-01 03:02:43.428 eqi, eqj = i, j
2025-07-01 03:02:43.433 continue
2025-07-01 03:02:43.437 cruncher.set_seq1(ai)
2025-07-01 03:02:43.442 # computing similarity is expensive, so use the quick
2025-07-01 03:02:43.446 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:43.450 # compares by a factor of 3.
2025-07-01 03:02:43.455 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:43.459 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:43.464 # of the computation is cached by cruncher
2025-07-01 03:02:43.468 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:43.473 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:43.477 cruncher.ratio() > best_ratio:
2025-07-01 03:02:43.482 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:43.486 if best_ratio < cutoff:
2025-07-01 03:02:43.491 # no non-identical "pretty close" pair
2025-07-01 03:02:43.495 if eqi is None:
2025-07-01 03:02:43.499 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:43.504 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:43.508 return
2025-07-01 03:02:43.513 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:43.517 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:43.521 else:
2025-07-01 03:02:43.526 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:43.530 eqi = None
2025-07-01 03:02:43.534
2025-07-01 03:02:43.539 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:43.543 # identical
2025-07-01 03:02:43.547
2025-07-01 03:02:43.552 # pump out diffs from before the synch point
2025-07-01 03:02:43.556 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:43.561
2025-07-01 03:02:43.565 # do intraline marking on the synch pair
2025-07-01 03:02:43.570 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:43.574 if eqi is None:
2025-07-01 03:02:43.579 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:43.583 atags = btags = ""
2025-07-01 03:02:43.588 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:43.593 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:43.597 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:43.602 if tag == 'replace':
2025-07-01 03:02:43.607 atags += '^' * la
2025-07-01 03:02:43.612 btags += '^' * lb
2025-07-01 03:02:43.616 elif tag == 'delete':
2025-07-01 03:02:43.621 atags += '-' * la
2025-07-01 03:02:43.626 elif tag == 'insert':
2025-07-01 03:02:43.630 btags += '+' * lb
2025-07-01 03:02:43.634 elif tag == 'equal':
2025-07-01 03:02:43.639 atags += ' ' * la
2025-07-01 03:02:43.643 btags += ' ' * lb
2025-07-01 03:02:43.648 else:
2025-07-01 03:02:43.652 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:43.657 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:43.661 else:
2025-07-01 03:02:43.665 # the synch pair is identical
2025-07-01 03:02:43.669 yield '  ' + aelt
2025-07-01 03:02:43.674
2025-07-01 03:02:43.678 # pump out diffs from after the synch point
2025-07-01 03:02:43.682 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:43.687
2025-07-01 03:02:43.691 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:43.695 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:43.700
2025-07-01 03:02:43.704 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:43.708 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:43.713 alo = 250, ahi = 1101
2025-07-01 03:02:43.718 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:43.722 blo = 250, bhi = 1101
2025-07-01 03:02:43.726
2025-07-01 03:02:43.731 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:43.735 g = []
2025-07-01 03:02:43.739 if alo < ahi:
2025-07-01 03:02:43.744 if blo < bhi:
2025-07-01 03:02:43.748 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:43.752 else:
2025-07-01 03:02:43.757 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:43.761 elif blo < bhi:
2025-07-01 03:02:43.766 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:43.770
2025-07-01 03:02:43.775 >       yield from g
2025-07-01 03:02:43.779
2025-07-01 03:02:43.783 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:43.788 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:43.792
2025-07-01 03:02:43.796 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:43.801 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:43.805 alo = 250, ahi = 1101
2025-07-01 03:02:43.810 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:43.814 blo = 250, bhi = 1101
2025-07-01 03:02:43.819
2025-07-01 03:02:43.826 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:43.838 r"""
2025-07-01 03:02:43.850 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:43.862 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:43.871 synch point, and intraline difference marking is done on the
2025-07-01 03:02:43.882 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:43.887
2025-07-01 03:02:43.891 Example:
2025-07-01 03:02:43.898
2025-07-01 03:02:43.911 >>> d = Differ()
2025-07-01 03:02:43.919 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:43.924 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:43.929 >>> print(''.join(results), end="")
2025-07-01 03:02:43.934 - abcDefghiJkl
2025-07-01 03:02:43.944 + abcdefGhijkl
2025-07-01 03:02:43.958 """
2025-07-01 03:02:43.963
2025-07-01 03:02:43.970 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:43.975 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:43.979 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:43.984 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:43.989 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:43.993
2025-07-01 03:02:43.998 # search for the pair that matches best without being identical
2025-07-01 03:02:44.003 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:44.008 # on junk -- unless we have to)
2025-07-01 03:02:44.012 for j in range(blo, bhi):
2025-07-01 03:02:44.017 bj = b[j]
2025-07-01 03:02:44.021 cruncher.set_seq2(bj)
2025-07-01 03:02:44.026 for i in range(alo, ahi):
2025-07-01 03:02:44.031 ai = a[i]
2025-07-01 03:02:44.035 if ai == bj:
2025-07-01 03:02:44.040 if eqi is None:
2025-07-01 03:02:44.044 eqi, eqj = i, j
2025-07-01 03:02:44.049 continue
2025-07-01 03:02:44.053 cruncher.set_seq1(ai)
2025-07-01 03:02:44.058 # computing similarity is expensive, so use the quick
2025-07-01 03:02:44.064 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:44.069 # compares by a factor of 3.
2025-07-01 03:02:44.075 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:44.081 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:44.088 # of the computation is cached by cruncher
2025-07-01 03:02:44.093 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:44.099 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:44.106 cruncher.ratio() > best_ratio:
2025-07-01 03:02:44.111 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:44.115 if best_ratio < cutoff:
2025-07-01 03:02:44.121 # no non-identical "pretty close" pair
2025-07-01 03:02:44.127 if eqi is None:
2025-07-01 03:02:44.133 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:44.139 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:44.144 return
2025-07-01 03:02:44.149 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:44.153 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:44.158 else:
2025-07-01 03:02:44.162 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:44.167 eqi = None
2025-07-01 03:02:44.171
2025-07-01 03:02:44.176 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:44.182 # identical
2025-07-01 03:02:44.187
2025-07-01 03:02:44.192 # pump out diffs from before the synch point
2025-07-01 03:02:44.197 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:44.202
2025-07-01 03:02:44.208 # do intraline marking on the synch pair
2025-07-01 03:02:44.213 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:44.218 if eqi is None:
2025-07-01 03:02:44.223 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:44.228 atags = btags = ""
2025-07-01 03:02:44.234 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:44.240 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:44.245 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:44.252 if tag == 'replace':
2025-07-01 03:02:44.258 atags += '^' * la
2025-07-01 03:02:44.263 btags += '^' * lb
2025-07-01 03:02:44.267 elif tag == 'delete':
2025-07-01 03:02:44.271 atags += '-' * la
2025-07-01 03:02:44.276 elif tag == 'insert':
2025-07-01 03:02:44.280 btags += '+' * lb
2025-07-01 03:02:44.285 elif tag == 'equal':
2025-07-01 03:02:44.289 atags += ' ' * la
2025-07-01 03:02:44.294 btags += ' ' * lb
2025-07-01 03:02:44.299 else:
2025-07-01 03:02:44.306 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:44.313 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:44.320 else:
2025-07-01 03:02:44.326 # the synch pair is identical
2025-07-01 03:02:44.333 yield '  ' + aelt
2025-07-01 03:02:44.339
2025-07-01 03:02:44.345 # pump out diffs from after the synch point
2025-07-01 03:02:44.351 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:44.356
2025-07-01 03:02:44.360 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:44.365 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:44.370
2025-07-01 03:02:44.374 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:44.379 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:44.384 alo = 251, ahi = 1101
2025-07-01 03:02:44.389 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:44.394 blo = 251, bhi = 1101
2025-07-01 03:02:44.398
2025-07-01 03:02:44.402 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:44.407 g = []
2025-07-01 03:02:44.411 if alo < ahi:
2025-07-01 03:02:44.416 if blo < bhi:
2025-07-01 03:02:44.420 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:44.425 else:
2025-07-01 03:02:44.429 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:44.434 elif blo < bhi:
2025-07-01 03:02:44.439 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:44.444
2025-07-01 03:02:44.448 >       yield from g
2025-07-01 03:02:44.452
2025-07-01 03:02:44.457 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:44.461 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:44.465
2025-07-01 03:02:44.470 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:44.475 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:44.479 alo = 251, ahi = 1101
2025-07-01 03:02:44.484 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:44.489 blo = 251, bhi = 1101
2025-07-01 03:02:44.493
2025-07-01 03:02:44.498 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:44.502 r"""
2025-07-01 03:02:44.507 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:44.512 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:44.516 synch point, and intraline difference marking is done on the
2025-07-01 03:02:44.521 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:44.525
2025-07-01 03:02:44.530 Example:
2025-07-01 03:02:44.535
2025-07-01 03:02:44.540 >>> d = Differ()
2025-07-01 03:02:44.544 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:44.550 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:44.556 >>> print(''.join(results), end="")
2025-07-01 03:02:44.561 - abcDefghiJkl
2025-07-01 03:02:44.570 + abcdefGhijkl
2025-07-01 03:02:44.580 """
2025-07-01 03:02:44.584
2025-07-01 03:02:44.589 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:44.594 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:44.598 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:44.603 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:44.607 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:44.612
2025-07-01 03:02:44.616 # search for the pair that matches best without being identical
2025-07-01 03:02:44.621 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:44.625 # on junk -- unless we have to)
2025-07-01 03:02:44.630 for j in range(blo, bhi):
2025-07-01 03:02:44.634 bj = b[j]
2025-07-01 03:02:44.639 cruncher.set_seq2(bj)
2025-07-01 03:02:44.644 for i in range(alo, ahi):
2025-07-01 03:02:44.648 ai = a[i]
2025-07-01 03:02:44.653 if ai == bj:
2025-07-01 03:02:44.657 if eqi is None:
2025-07-01 03:02:44.661 eqi, eqj = i, j
2025-07-01 03:02:44.666 continue
2025-07-01 03:02:44.670 cruncher.set_seq1(ai)
2025-07-01 03:02:44.674 # computing similarity is expensive, so use the quick
2025-07-01 03:02:44.679 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:44.683 # compares by a factor of 3.
2025-07-01 03:02:44.688 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:44.692 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:44.697 # of the computation is cached by cruncher
2025-07-01 03:02:44.701 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:44.705 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:44.710 cruncher.ratio() > best_ratio:
2025-07-01 03:02:44.714 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:44.719 if best_ratio < cutoff:
2025-07-01 03:02:44.723 # no non-identical "pretty close" pair
2025-07-01 03:02:44.727 if eqi is None:
2025-07-01 03:02:44.732 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:44.736 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:44.741 return
2025-07-01 03:02:44.745 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:44.750 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:44.754 else:
2025-07-01 03:02:44.759 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:44.763 eqi = None
2025-07-01 03:02:44.767
2025-07-01 03:02:44.771 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:44.776 # identical
2025-07-01 03:02:44.780
2025-07-01 03:02:44.784 # pump out diffs from before the synch point
2025-07-01 03:02:44.789 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:44.793
2025-07-01 03:02:44.797 # do intraline marking on the synch pair
2025-07-01 03:02:44.802 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:44.806 if eqi is None:
2025-07-01 03:02:44.810 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:44.815 atags = btags = ""
2025-07-01 03:02:44.819 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:44.823 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:44.828 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:44.832 if tag == 'replace':
2025-07-01 03:02:44.836 atags += '^' * la
2025-07-01 03:02:44.841 btags += '^' * lb
2025-07-01 03:02:44.845 elif tag == 'delete':
2025-07-01 03:02:44.849 atags += '-' * la
2025-07-01 03:02:44.854 elif tag == 'insert':
2025-07-01 03:02:44.858 btags += '+' * lb
2025-07-01 03:02:44.863 elif tag == 'equal':
2025-07-01 03:02:44.867 atags += ' ' * la
2025-07-01 03:02:44.872 btags += ' ' * lb
2025-07-01 03:02:44.876 else:
2025-07-01 03:02:44.880 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:44.885 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:44.889 else:
2025-07-01 03:02:44.894 # the synch pair is identical
2025-07-01 03:02:44.898 yield '  ' + aelt
2025-07-01 03:02:44.902
2025-07-01 03:02:44.906 # pump out diffs from after the synch point
2025-07-01 03:02:44.911 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:44.916
2025-07-01 03:02:44.921 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:44.925 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:44.929
2025-07-01 03:02:44.935 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:44.939 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:44.944 alo = 252, ahi = 1101
2025-07-01 03:02:44.948 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:44.953 blo = 252, bhi = 1101
2025-07-01 03:02:44.957
2025-07-01 03:02:44.962 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:44.968 g = []
2025-07-01 03:02:44.972 if alo < ahi:
2025-07-01 03:02:44.976 if blo < bhi:
2025-07-01 03:02:44.981 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:44.985 else:
2025-07-01 03:02:44.990 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:44.994 elif blo < bhi:
2025-07-01 03:02:44.999 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:45.004
2025-07-01 03:02:45.008 >       yield from g
2025-07-01 03:02:45.013
2025-07-01 03:02:45.017 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:45.022 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:45.026
2025-07-01 03:02:45.031 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:45.036 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:45.041 alo = 252, ahi = 1101
2025-07-01 03:02:45.046 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:45.050 blo = 252, bhi = 1101
2025-07-01 03:02:45.055
2025-07-01 03:02:45.059 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:45.064 r"""
2025-07-01 03:02:45.068 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:45.073 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:45.077 synch point, and intraline difference marking is done on the
2025-07-01 03:02:45.082 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:45.086
2025-07-01 03:02:45.090 Example:
2025-07-01 03:02:45.095
2025-07-01 03:02:45.099 >>> d = Differ()
2025-07-01 03:02:45.104 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:45.108 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:45.113 >>> print(''.join(results), end="")
2025-07-01 03:02:45.118 - abcDefghiJkl
2025-07-01 03:02:45.127 + abcdefGhijkl
2025-07-01 03:02:45.135 """
2025-07-01 03:02:45.139
2025-07-01 03:02:45.144 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:45.149 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:45.154 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:45.158 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:45.163 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:45.167
2025-07-01 03:02:45.172 # search for the pair that matches best without being identical
2025-07-01 03:02:45.176 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:45.181 # on junk -- unless we have to)
2025-07-01 03:02:45.186 for j in range(blo, bhi):
2025-07-01 03:02:45.190 bj = b[j]
2025-07-01 03:02:45.195 cruncher.set_seq2(bj)
2025-07-01 03:02:45.199 for i in range(alo, ahi):
2025-07-01 03:02:45.204 ai = a[i]
2025-07-01 03:02:45.208 if ai == bj:
2025-07-01 03:02:45.213 if eqi is None:
2025-07-01 03:02:45.217 eqi, eqj = i, j
2025-07-01 03:02:45.222 continue
2025-07-01 03:02:45.226 cruncher.set_seq1(ai)
2025-07-01 03:02:45.231 # computing similarity is expensive, so use the quick
2025-07-01 03:02:45.235 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:45.240 # compares by a factor of 3.
2025-07-01 03:02:45.244 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:45.249 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:45.254 # of the computation is cached by cruncher
2025-07-01 03:02:45.258 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:45.263 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:45.267 cruncher.ratio() > best_ratio:
2025-07-01 03:02:45.272 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:45.276 if best_ratio < cutoff:
2025-07-01 03:02:45.280 # no non-identical "pretty close" pair
2025-07-01 03:02:45.285 if eqi is None:
2025-07-01 03:02:45.289 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:45.294 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:45.298 return
2025-07-01 03:02:45.302 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:45.307 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:45.311 else:
2025-07-01 03:02:45.316 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:45.320 eqi = None
2025-07-01 03:02:45.324
2025-07-01 03:02:45.329 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:45.333 # identical
2025-07-01 03:02:45.338
2025-07-01 03:02:45.342 # pump out diffs from before the synch point
2025-07-01 03:02:45.347 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:45.351
2025-07-01 03:02:45.356 # do intraline marking on the synch pair
2025-07-01 03:02:45.360 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:45.364 if eqi is None:
2025-07-01 03:02:45.369 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:45.374 atags = btags = ""
2025-07-01 03:02:45.379 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:45.385 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:45.390 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:45.394 if tag == 'replace':
2025-07-01 03:02:45.399 atags += '^' * la
2025-07-01 03:02:45.403 btags += '^' * lb
2025-07-01 03:02:45.408 elif tag == 'delete':
2025-07-01 03:02:45.413 atags += '-' * la
2025-07-01 03:02:45.417 elif tag == 'insert':
2025-07-01 03:02:45.421 btags += '+' * lb
2025-07-01 03:02:45.426 elif tag == 'equal':
2025-07-01 03:02:45.430 atags += ' ' * la
2025-07-01 03:02:45.435 btags += ' ' * lb
2025-07-01 03:02:45.439 else:
2025-07-01 03:02:45.444 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:45.449 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:45.453 else:
2025-07-01 03:02:45.458 # the synch pair is identical
2025-07-01 03:02:45.462 yield '  ' + aelt
2025-07-01 03:02:45.467
2025-07-01 03:02:45.471 # pump out diffs from after the synch point
2025-07-01 03:02:45.476 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:45.480
2025-07-01 03:02:45.485 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:45.490 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:45.495
2025-07-01 03:02:45.499 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:45.504 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:45.509 alo = 253, ahi = 1101
2025-07-01 03:02:45.514 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:45.519 blo = 253, bhi = 1101
2025-07-01 03:02:45.523
2025-07-01 03:02:45.527 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:45.532 g = []
2025-07-01 03:02:45.536 if alo < ahi:
2025-07-01 03:02:45.541 if blo < bhi:
2025-07-01 03:02:45.545 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:45.550 else:
2025-07-01 03:02:45.554 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:45.559 elif blo < bhi:
2025-07-01 03:02:45.564 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:45.568
2025-07-01 03:02:45.573 >       yield from g
2025-07-01 03:02:45.577
2025-07-01 03:02:45.582 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:45.587 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:45.591
2025-07-01 03:02:45.596 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:45.601 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:45.606 alo = 253, ahi = 1101
2025-07-01 03:02:45.612 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:45.617 blo = 253, bhi = 1101
2025-07-01 03:02:45.622
2025-07-01 03:02:45.626 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:45.631 r"""
2025-07-01 03:02:45.636 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:45.640 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:45.645 synch point, and intraline difference marking is done on the
2025-07-01 03:02:45.649 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:45.654
2025-07-01 03:02:45.658 Example:
2025-07-01 03:02:45.663
2025-07-01 03:02:45.668 >>> d = Differ()
2025-07-01 03:02:45.675 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:45.680 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:45.684 >>> print(''.join(results), end="")
2025-07-01 03:02:45.689 - abcDefghiJkl
2025-07-01 03:02:45.698 + abcdefGhijkl
2025-07-01 03:02:45.706 """
2025-07-01 03:02:45.711
2025-07-01 03:02:45.715 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:45.720 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:45.725 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:45.729 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:45.734 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:45.738
2025-07-01 03:02:45.742 # search for the pair that matches best without being identical
2025-07-01 03:02:45.747 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:45.751 # on junk -- unless we have to)
2025-07-01 03:02:45.756 for j in range(blo, bhi):
2025-07-01 03:02:45.760 bj = b[j]
2025-07-01 03:02:45.764 cruncher.set_seq2(bj)
2025-07-01 03:02:45.769 for i in range(alo, ahi):
2025-07-01 03:02:45.773 ai = a[i]
2025-07-01 03:02:45.777 if ai == bj:
2025-07-01 03:02:45.781 if eqi is None:
2025-07-01 03:02:45.786 eqi, eqj = i, j
2025-07-01 03:02:45.790 continue
2025-07-01 03:02:45.794 cruncher.set_seq1(ai)
2025-07-01 03:02:45.799 # computing similarity is expensive, so use the quick
2025-07-01 03:02:45.803 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:45.807 # compares by a factor of 3.
2025-07-01 03:02:45.812 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:45.816 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:45.820 # of the computation is cached by cruncher
2025-07-01 03:02:45.825 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:45.829 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:45.833 cruncher.ratio() > best_ratio:
2025-07-01 03:02:45.838 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:45.842 if best_ratio < cutoff:
2025-07-01 03:02:45.846 # no non-identical "pretty close" pair
2025-07-01 03:02:45.850 if eqi is None:
2025-07-01 03:02:45.855 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:45.859 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:45.864 return
2025-07-01 03:02:45.868 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:45.872 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:45.877 else:
2025-07-01 03:02:45.881 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:45.885 eqi = None
2025-07-01 03:02:45.889
2025-07-01 03:02:45.894 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:45.898 # identical
2025-07-01 03:02:45.902
2025-07-01 03:02:45.906 # pump out diffs from before the synch point
2025-07-01 03:02:45.911 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:45.915
2025-07-01 03:02:45.919 # do intraline marking on the synch pair
2025-07-01 03:02:45.924 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:45.928 if eqi is None:
2025-07-01 03:02:45.932 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:45.937 atags = btags = ""
2025-07-01 03:02:45.941 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:45.945 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:45.950 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:45.954 if tag == 'replace':
2025-07-01 03:02:45.959 atags += '^' * la
2025-07-01 03:02:45.963 btags += '^' * lb
2025-07-01 03:02:45.967 elif tag == 'delete':
2025-07-01 03:02:45.972 atags += '-' * la
2025-07-01 03:02:45.976 elif tag == 'insert':
2025-07-01 03:02:45.980 btags += '+' * lb
2025-07-01 03:02:45.984 elif tag == 'equal':
2025-07-01 03:02:45.989 atags += ' ' * la
2025-07-01 03:02:45.993 btags += ' ' * lb
2025-07-01 03:02:45.997 else:
2025-07-01 03:02:46.002 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:46.006 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:46.011 else:
2025-07-01 03:02:46.015 # the synch pair is identical
2025-07-01 03:02:46.019 yield '  ' + aelt
2025-07-01 03:02:46.023
2025-07-01 03:02:46.028 # pump out diffs from after the synch point
2025-07-01 03:02:46.032 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:46.037
2025-07-01 03:02:46.041 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:46.045 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:46.050
2025-07-01 03:02:46.054 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:46.059 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:46.063 alo = 254, ahi = 1101
2025-07-01 03:02:46.068 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:46.073 blo = 254, bhi = 1101
2025-07-01 03:02:46.077
2025-07-01 03:02:46.081 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:46.085 g = []
2025-07-01 03:02:46.090 if alo < ahi:
2025-07-01 03:02:46.094 if blo < bhi:
2025-07-01 03:02:46.098 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:46.103 else:
2025-07-01 03:02:46.107 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:46.111 elif blo < bhi:
2025-07-01 03:02:46.116 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:46.120
2025-07-01 03:02:46.124 >       yield from g
2025-07-01 03:02:46.128
2025-07-01 03:02:46.133 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:46.137 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:46.142
2025-07-01 03:02:46.146 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:46.151 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:46.155 alo = 254, ahi = 1101
2025-07-01 03:02:46.160 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:46.165 blo = 254, bhi = 1101
2025-07-01 03:02:46.169
2025-07-01 03:02:46.174 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:46.178 r"""
2025-07-01 03:02:46.182 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:46.187 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:46.191 synch point, and intraline difference marking is done on the
2025-07-01 03:02:46.196 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:46.200
2025-07-01 03:02:46.205 Example:
2025-07-01 03:02:46.209
2025-07-01 03:02:46.213 >>> d = Differ()
2025-07-01 03:02:46.219 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:46.223 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:46.228 >>> print(''.join(results), end="")
2025-07-01 03:02:46.232 - abcDefghiJkl
2025-07-01 03:02:46.241 + abcdefGhijkl
2025-07-01 03:02:46.250 """
2025-07-01 03:02:46.254
2025-07-01 03:02:46.259 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:46.264 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:46.269 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:46.274 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:46.278 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:46.283
2025-07-01 03:02:46.287 # search for the pair that matches best without being identical
2025-07-01 03:02:46.292 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:46.297 # on junk -- unless we have to)
2025-07-01 03:02:46.301 for j in range(blo, bhi):
2025-07-01 03:02:46.306 bj = b[j]
2025-07-01 03:02:46.311 cruncher.set_seq2(bj)
2025-07-01 03:02:46.316 for i in range(alo, ahi):
2025-07-01 03:02:46.320 ai = a[i]
2025-07-01 03:02:46.325 if ai == bj:
2025-07-01 03:02:46.330 if eqi is None:
2025-07-01 03:02:46.335 eqi, eqj = i, j
2025-07-01 03:02:46.340 continue
2025-07-01 03:02:46.345 cruncher.set_seq1(ai)
2025-07-01 03:02:46.350 # computing similarity is expensive, so use the quick
2025-07-01 03:02:46.355 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:46.362 # compares by a factor of 3.
2025-07-01 03:02:46.370 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:46.375 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:46.380 # of the computation is cached by cruncher
2025-07-01 03:02:46.385 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:46.391 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:46.397 cruncher.ratio() > best_ratio:
2025-07-01 03:02:46.402 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:46.407 if best_ratio < cutoff:
2025-07-01 03:02:46.412 # no non-identical "pretty close" pair
2025-07-01 03:02:46.418 if eqi is None:
2025-07-01 03:02:46.423 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:46.428 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:46.433 return
2025-07-01 03:02:46.438 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:46.443 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:46.448 else:
2025-07-01 03:02:46.454 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:46.458 eqi = None
2025-07-01 03:02:46.463
2025-07-01 03:02:46.468 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:46.473 # identical
2025-07-01 03:02:46.477
2025-07-01 03:02:46.482 # pump out diffs from before the synch point
2025-07-01 03:02:46.487 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:46.491
2025-07-01 03:02:46.496 # do intraline marking on the synch pair
2025-07-01 03:02:46.501 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:46.506 if eqi is None:
2025-07-01 03:02:46.511 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:46.516 atags = btags = ""
2025-07-01 03:02:46.520 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:46.526 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:46.530 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:46.535 if tag == 'replace':
2025-07-01 03:02:46.540 atags += '^' * la
2025-07-01 03:02:46.544 btags += '^' * lb
2025-07-01 03:02:46.549 elif tag == 'delete':
2025-07-01 03:02:46.553 atags += '-' * la
2025-07-01 03:02:46.558 elif tag == 'insert':
2025-07-01 03:02:46.563 btags += '+' * lb
2025-07-01 03:02:46.567 elif tag == 'equal':
2025-07-01 03:02:46.572 atags += ' ' * la
2025-07-01 03:02:46.576 btags += ' ' * lb
2025-07-01 03:02:46.581 else:
2025-07-01 03:02:46.585 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:46.590 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:46.594 else:
2025-07-01 03:02:46.599 # the synch pair is identical
2025-07-01 03:02:46.604 yield '  ' + aelt
2025-07-01 03:02:46.609
2025-07-01 03:02:46.613 # pump out diffs from after the synch point
2025-07-01 03:02:46.618 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:46.623
2025-07-01 03:02:46.628 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:46.632 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:46.637
2025-07-01 03:02:46.641 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:46.646 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:46.650 alo = 255, ahi = 1101
2025-07-01 03:02:46.655 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:46.660 blo = 255, bhi = 1101
2025-07-01 03:02:46.664
2025-07-01 03:02:46.669 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:46.673 g = []
2025-07-01 03:02:46.678 if alo < ahi:
2025-07-01 03:02:46.682 if blo < bhi:
2025-07-01 03:02:46.687 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:46.691 else:
2025-07-01 03:02:46.696 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:46.700 elif blo < bhi:
2025-07-01 03:02:46.705 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:46.710
2025-07-01 03:02:46.714 >       yield from g
2025-07-01 03:02:46.719
2025-07-01 03:02:46.723 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:46.728 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:46.732
2025-07-01 03:02:46.737 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:46.743 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:46.747 alo = 255, ahi = 1101
2025-07-01 03:02:46.752 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:46.758 blo = 255, bhi = 1101
2025-07-01 03:02:46.762
2025-07-01 03:02:46.767 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:46.771 r"""
2025-07-01 03:02:46.776 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:46.781 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:46.786 synch point, and intraline difference marking is done on the
2025-07-01 03:02:46.790 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:46.794
2025-07-01 03:02:46.799 Example:
2025-07-01 03:02:46.804
2025-07-01 03:02:46.808 >>> d = Differ()
2025-07-01 03:02:46.813 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:46.819 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:46.824 >>> print(''.join(results), end="")
2025-07-01 03:02:46.829 - abcDefghiJkl
2025-07-01 03:02:46.838 + abcdefGhijkl
2025-07-01 03:02:46.847 """
2025-07-01 03:02:46.852
2025-07-01 03:02:46.857 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:46.862 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:46.867 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:46.873 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:46.879 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:46.885
2025-07-01 03:02:46.892 # search for the pair that matches best without being identical
2025-07-01 03:02:46.898 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:46.904 # on junk -- unless we have to)
2025-07-01 03:02:46.910 for j in range(blo, bhi):
2025-07-01 03:02:46.916 bj = b[j]
2025-07-01 03:02:46.922 cruncher.set_seq2(bj)
2025-07-01 03:02:46.928 for i in range(alo, ahi):
2025-07-01 03:02:46.934 ai = a[i]
2025-07-01 03:02:46.939 if ai == bj:
2025-07-01 03:02:46.945 if eqi is None:
2025-07-01 03:02:46.951 eqi, eqj = i, j
2025-07-01 03:02:46.957 continue
2025-07-01 03:02:46.963 cruncher.set_seq1(ai)
2025-07-01 03:02:46.968 # computing similarity is expensive, so use the quick
2025-07-01 03:02:46.974 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:46.980 # compares by a factor of 3.
2025-07-01 03:02:46.985 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:46.990 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:46.995 # of the computation is cached by cruncher
2025-07-01 03:02:46.999 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:47.005 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:47.009 cruncher.ratio() > best_ratio:
2025-07-01 03:02:47.014 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:47.019 if best_ratio < cutoff:
2025-07-01 03:02:47.023 # no non-identical "pretty close" pair
2025-07-01 03:02:47.028 if eqi is None:
2025-07-01 03:02:47.033 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:47.037 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:47.042 return
2025-07-01 03:02:47.046 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:47.051 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:47.055 else:
2025-07-01 03:02:47.059 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:47.064 eqi = None
2025-07-01 03:02:47.068
2025-07-01 03:02:47.073 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:47.077 # identical
2025-07-01 03:02:47.081
2025-07-01 03:02:47.086 # pump out diffs from before the synch point
2025-07-01 03:02:47.090 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:47.095
2025-07-01 03:02:47.099 # do intraline marking on the synch pair
2025-07-01 03:02:47.103 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:47.108 if eqi is None:
2025-07-01 03:02:47.112 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:47.117 atags = btags = ""
2025-07-01 03:02:47.121 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:47.125 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:47.130 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:47.134 if tag == 'replace':
2025-07-01 03:02:47.139 atags += '^' * la
2025-07-01 03:02:47.143 btags += '^' * lb
2025-07-01 03:02:47.148 elif tag == 'delete':
2025-07-01 03:02:47.152 atags += '-' * la
2025-07-01 03:02:47.157 elif tag == 'insert':
2025-07-01 03:02:47.162 btags += '+' * lb
2025-07-01 03:02:47.167 elif tag == 'equal':
2025-07-01 03:02:47.171 atags += ' ' * la
2025-07-01 03:02:47.176 btags += ' ' * lb
2025-07-01 03:02:47.180 else:
2025-07-01 03:02:47.185 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:47.190 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:47.194 else:
2025-07-01 03:02:47.198 # the synch pair is identical
2025-07-01 03:02:47.203 yield '  ' + aelt
2025-07-01 03:02:47.207
2025-07-01 03:02:47.212 # pump out diffs from after the synch point
2025-07-01 03:02:47.217 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:47.221
2025-07-01 03:02:47.226 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:47.231 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:47.235
2025-07-01 03:02:47.240 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:47.245 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:47.250 alo = 256, ahi = 1101
2025-07-01 03:02:47.256 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:47.262 blo = 256, bhi = 1101
2025-07-01 03:02:47.268
2025-07-01 03:02:47.274 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:47.281 g = []
2025-07-01 03:02:47.287 if alo < ahi:
2025-07-01 03:02:47.293 if blo < bhi:
2025-07-01 03:02:47.299 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:47.305 else:
2025-07-01 03:02:47.312 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:47.318 elif blo < bhi:
2025-07-01 03:02:47.323 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:47.328
2025-07-01 03:02:47.333 >       yield from g
2025-07-01 03:02:47.339
2025-07-01 03:02:47.345 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:47.350 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:47.354
2025-07-01 03:02:47.359 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:47.363 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:47.369 alo = 256, ahi = 1101
2025-07-01 03:02:47.375 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:47.380 blo = 256, bhi = 1101
2025-07-01 03:02:47.384
2025-07-01 03:02:47.390 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:47.395 r"""
2025-07-01 03:02:47.401 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:47.407 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:47.413 synch point, and intraline difference marking is done on the
2025-07-01 03:02:47.418 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:47.423
2025-07-01 03:02:47.430 Example:
2025-07-01 03:02:47.438
2025-07-01 03:02:47.444 >>> d = Differ()
2025-07-01 03:02:47.450 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:47.456 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:47.461 >>> print(''.join(results), end="")
2025-07-01 03:02:47.466 - abcDefghiJkl
2025-07-01 03:02:47.475 + abcdefGhijkl
2025-07-01 03:02:47.484 """
2025-07-01 03:02:47.488
2025-07-01 03:02:47.493 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:47.498 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:47.502 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:47.507 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:47.512 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:47.517
2025-07-01 03:02:47.522 # search for the pair that matches best without being identical
2025-07-01 03:02:47.528 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:47.534 # on junk -- unless we have to)
2025-07-01 03:02:47.539 for j in range(blo, bhi):
2025-07-01 03:02:47.545 bj = b[j]
2025-07-01 03:02:47.550 cruncher.set_seq2(bj)
2025-07-01 03:02:47.556 for i in range(alo, ahi):
2025-07-01 03:02:47.562 ai = a[i]
2025-07-01 03:02:47.567 if ai == bj:
2025-07-01 03:02:47.572 if eqi is None:
2025-07-01 03:02:47.577 eqi, eqj = i, j
2025-07-01 03:02:47.583 continue
2025-07-01 03:02:47.588 cruncher.set_seq1(ai)
2025-07-01 03:02:47.593 # computing similarity is expensive, so use the quick
2025-07-01 03:02:47.598 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:47.602 # compares by a factor of 3.
2025-07-01 03:02:47.607 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:47.611 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:47.616 # of the computation is cached by cruncher
2025-07-01 03:02:47.622 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:47.628 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:47.633 cruncher.ratio() > best_ratio:
2025-07-01 03:02:47.638 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:47.643 if best_ratio < cutoff:
2025-07-01 03:02:47.648 # no non-identical "pretty close" pair
2025-07-01 03:02:47.653 if eqi is None:
2025-07-01 03:02:47.659 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:47.663 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:47.669 return
2025-07-01 03:02:47.675 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:47.679 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:47.684 else:
2025-07-01 03:02:47.690 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:47.694 eqi = None
2025-07-01 03:02:47.700
2025-07-01 03:02:47.705 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:47.711 # identical
2025-07-01 03:02:47.716
2025-07-01 03:02:47.721 # pump out diffs from before the synch point
2025-07-01 03:02:47.726 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:47.731
2025-07-01 03:02:47.737 # do intraline marking on the synch pair
2025-07-01 03:02:47.742 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:47.747 if eqi is None:
2025-07-01 03:02:47.753 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:47.759 atags = btags = ""
2025-07-01 03:02:47.764 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:47.770 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:47.775 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:47.780 if tag == 'replace':
2025-07-01 03:02:47.786 atags += '^' * la
2025-07-01 03:02:47.792 btags += '^' * lb
2025-07-01 03:02:47.796 elif tag == 'delete':
2025-07-01 03:02:47.801 atags += '-' * la
2025-07-01 03:02:47.807 elif tag == 'insert':
2025-07-01 03:02:47.813 btags += '+' * lb
2025-07-01 03:02:47.818 elif tag == 'equal':
2025-07-01 03:02:47.823 atags += ' ' * la
2025-07-01 03:02:47.829 btags += ' ' * lb
2025-07-01 03:02:47.834 else:
2025-07-01 03:02:47.840 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:47.846 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:47.851 else:
2025-07-01 03:02:47.856 # the synch pair is identical
2025-07-01 03:02:47.861 yield '  ' + aelt
2025-07-01 03:02:47.867
2025-07-01 03:02:47.872 # pump out diffs from after the synch point
2025-07-01 03:02:47.878 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:47.883
2025-07-01 03:02:47.889 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:47.894 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:47.899
2025-07-01 03:02:47.903 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:47.909 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:47.915 alo = 257, ahi = 1101
2025-07-01 03:02:47.920 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:47.926 blo = 257, bhi = 1101
2025-07-01 03:02:47.931
2025-07-01 03:02:47.935 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:47.941 g = []
2025-07-01 03:02:47.946 if alo < ahi:
2025-07-01 03:02:47.951 if blo < bhi:
2025-07-01 03:02:47.957 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:47.962 else:
2025-07-01 03:02:47.968 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:47.973 elif blo < bhi:
2025-07-01 03:02:47.979 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:47.984
2025-07-01 03:02:47.988 >       yield from g
2025-07-01 03:02:47.993
2025-07-01 03:02:47.998 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:48.003 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:48.007
2025-07-01 03:02:48.013 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:48.018 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:48.023 alo = 257, ahi = 1101
2025-07-01 03:02:48.030 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:48.035 blo = 257, bhi = 1101
2025-07-01 03:02:48.040
2025-07-01 03:02:48.045 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:48.050 r"""
2025-07-01 03:02:48.055 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:48.061 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:48.066 synch point, and intraline difference marking is done on the
2025-07-01 03:02:48.071 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:48.077
2025-07-01 03:02:48.082 Example:
2025-07-01 03:02:48.095
2025-07-01 03:02:48.107 >>> d = Differ()
2025-07-01 03:02:48.113 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:48.118 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:48.123 >>> print(''.join(results), end="")
2025-07-01 03:02:48.129 - abcDefghiJkl
2025-07-01 03:02:48.139 + abcdefGhijkl
2025-07-01 03:02:48.150 """
2025-07-01 03:02:48.155
2025-07-01 03:02:48.160 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:48.166 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:48.170 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:48.175 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:48.181 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:48.186
2025-07-01 03:02:48.191 # search for the pair that matches best without being identical
2025-07-01 03:02:48.197 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:48.202 # on junk -- unless we have to)
2025-07-01 03:02:48.207 for j in range(blo, bhi):
2025-07-01 03:02:48.212 bj = b[j]
2025-07-01 03:02:48.217 cruncher.set_seq2(bj)
2025-07-01 03:02:48.222 for i in range(alo, ahi):
2025-07-01 03:02:48.228 ai = a[i]
2025-07-01 03:02:48.233 if ai == bj:
2025-07-01 03:02:48.239 if eqi is None:
2025-07-01 03:02:48.245 eqi, eqj = i, j
2025-07-01 03:02:48.250 continue
2025-07-01 03:02:48.255 cruncher.set_seq1(ai)
2025-07-01 03:02:48.260 # computing similarity is expensive, so use the quick
2025-07-01 03:02:48.266 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:48.271 # compares by a factor of 3.
2025-07-01 03:02:48.277 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:48.282 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:48.288 # of the computation is cached by cruncher
2025-07-01 03:02:48.293 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:48.298 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:48.304 cruncher.ratio() > best_ratio:
2025-07-01 03:02:48.311 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:48.316 if best_ratio < cutoff:
2025-07-01 03:02:48.321 # no non-identical "pretty close" pair
2025-07-01 03:02:48.326 if eqi is None:
2025-07-01 03:02:48.332 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:48.338 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:48.342 return
2025-07-01 03:02:48.347 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:48.352 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:48.357 else:
2025-07-01 03:02:48.362 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:48.367 eqi = None
2025-07-01 03:02:48.371
2025-07-01 03:02:48.377 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:48.382 # identical
2025-07-01 03:02:48.387
2025-07-01 03:02:48.393 # pump out diffs from before the synch point
2025-07-01 03:02:48.399 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:48.403
2025-07-01 03:02:48.408 # do intraline marking on the synch pair
2025-07-01 03:02:48.414 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:48.420 if eqi is None:
2025-07-01 03:02:48.425 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:48.430 atags = btags = ""
2025-07-01 03:02:48.436 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:48.442 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:48.447 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:48.451 if tag == 'replace':
2025-07-01 03:02:48.455 atags += '^' * la
2025-07-01 03:02:48.460 btags += '^' * lb
2025-07-01 03:02:48.464 elif tag == 'delete':
2025-07-01 03:02:48.469 atags += '-' * la
2025-07-01 03:02:48.473 elif tag == 'insert':
2025-07-01 03:02:48.477 btags += '+' * lb
2025-07-01 03:02:48.482 elif tag == 'equal':
2025-07-01 03:02:48.486 atags += ' ' * la
2025-07-01 03:02:48.490 btags += ' ' * lb
2025-07-01 03:02:48.495 else:
2025-07-01 03:02:48.499 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:48.504 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:48.508 else:
2025-07-01 03:02:48.512 # the synch pair is identical
2025-07-01 03:02:48.517 yield '  ' + aelt
2025-07-01 03:02:48.522
2025-07-01 03:02:48.526 # pump out diffs from after the synch point
2025-07-01 03:02:48.531 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:48.535
2025-07-01 03:02:48.540 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:48.544 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:48.549
2025-07-01 03:02:48.553 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:48.558 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:48.563 alo = 258, ahi = 1101
2025-07-01 03:02:48.567 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:48.572 blo = 258, bhi = 1101
2025-07-01 03:02:48.576
2025-07-01 03:02:48.580 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:48.585 g = []
2025-07-01 03:02:48.589 if alo < ahi:
2025-07-01 03:02:48.593 if blo < bhi:
2025-07-01 03:02:48.598 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:48.602 else:
2025-07-01 03:02:48.608 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:48.614 elif blo < bhi:
2025-07-01 03:02:48.619 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:48.624
2025-07-01 03:02:48.630 >       yield from g
2025-07-01 03:02:48.634
2025-07-01 03:02:48.640 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:48.645 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:48.650
2025-07-01 03:02:48.656 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:48.662 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:48.667 alo = 258, ahi = 1101
2025-07-01 03:02:48.673 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:48.678 blo = 258, bhi = 1101
2025-07-01 03:02:48.683
2025-07-01 03:02:48.689 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:48.694 r"""
2025-07-01 03:02:48.699 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:48.704 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:48.709 synch point, and intraline difference marking is done on the
2025-07-01 03:02:48.714 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:48.718
2025-07-01 03:02:48.722 Example:
2025-07-01 03:02:48.726
2025-07-01 03:02:48.731 >>> d = Differ()
2025-07-01 03:02:48.735 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:48.740 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:48.744 >>> print(''.join(results), end="")
2025-07-01 03:02:48.749 - abcDefghiJkl
2025-07-01 03:02:48.757 + abcdefGhijkl
2025-07-01 03:02:48.766 """
2025-07-01 03:02:48.771
2025-07-01 03:02:48.775 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:48.780 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:48.784 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:48.788 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:48.793 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:48.797
2025-07-01 03:02:48.802 # search for the pair that matches best without being identical
2025-07-01 03:02:48.806 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:48.811 # on junk -- unless we have to)
2025-07-01 03:02:48.815 for j in range(blo, bhi):
2025-07-01 03:02:48.819 bj = b[j]
2025-07-01 03:02:48.825 cruncher.set_seq2(bj)
2025-07-01 03:02:48.830 for i in range(alo, ahi):
2025-07-01 03:02:48.836 ai = a[i]
2025-07-01 03:02:48.840 if ai == bj:
2025-07-01 03:02:48.845 if eqi is None:
2025-07-01 03:02:48.849 eqi, eqj = i, j
2025-07-01 03:02:48.854 continue
2025-07-01 03:02:48.858 cruncher.set_seq1(ai)
2025-07-01 03:02:48.862 # computing similarity is expensive, so use the quick
2025-07-01 03:02:48.867 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:48.871 # compares by a factor of 3.
2025-07-01 03:02:48.875 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:48.880 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:48.884 # of the computation is cached by cruncher
2025-07-01 03:02:48.889 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:48.893 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:48.897 cruncher.ratio() > best_ratio:
2025-07-01 03:02:48.902 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:48.906 if best_ratio < cutoff:
2025-07-01 03:02:48.910 # no non-identical "pretty close" pair
2025-07-01 03:02:48.915 if eqi is None:
2025-07-01 03:02:48.919 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:48.923 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:48.928 return
2025-07-01 03:02:48.932 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:48.937 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:48.943 else:
2025-07-01 03:02:48.948 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:48.952 eqi = None
2025-07-01 03:02:48.957
2025-07-01 03:02:48.961 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:48.965 # identical
2025-07-01 03:02:48.970
2025-07-01 03:02:48.974 # pump out diffs from before the synch point
2025-07-01 03:02:48.978 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:48.983
2025-07-01 03:02:48.987 # do intraline marking on the synch pair
2025-07-01 03:02:48.991 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:48.995 if eqi is None:
2025-07-01 03:02:49.000 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:49.004 atags = btags = ""
2025-07-01 03:02:49.009 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:49.013 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:49.017 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:49.022 if tag == 'replace':
2025-07-01 03:02:49.026 atags += '^' * la
2025-07-01 03:02:49.031 btags += '^' * lb
2025-07-01 03:02:49.037 elif tag == 'delete':
2025-07-01 03:02:49.042 atags += '-' * la
2025-07-01 03:02:49.047 elif tag == 'insert':
2025-07-01 03:02:49.052 btags += '+' * lb
2025-07-01 03:02:49.056 elif tag == 'equal':
2025-07-01 03:02:49.060 atags += ' ' * la
2025-07-01 03:02:49.065 btags += ' ' * lb
2025-07-01 03:02:49.069 else:
2025-07-01 03:02:49.073 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:49.078 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:49.082 else:
2025-07-01 03:02:49.087 # the synch pair is identical
2025-07-01 03:02:49.091 yield '  ' + aelt
2025-07-01 03:02:49.096
2025-07-01 03:02:49.100 # pump out diffs from after the synch point
2025-07-01 03:02:49.105 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:49.109
2025-07-01 03:02:49.113 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:49.118 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:49.122
2025-07-01 03:02:49.127 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:49.131 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:49.137 alo = 259, ahi = 1101
2025-07-01 03:02:49.141 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:49.146 blo = 259, bhi = 1101
2025-07-01 03:02:49.150
2025-07-01 03:02:49.154 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:49.159 g = []
2025-07-01 03:02:49.163 if alo < ahi:
2025-07-01 03:02:49.167 if blo < bhi:
2025-07-01 03:02:49.172 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:49.176 else:
2025-07-01 03:02:49.181 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:49.185 elif blo < bhi:
2025-07-01 03:02:49.190 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:49.195
2025-07-01 03:02:49.199 >       yield from g
2025-07-01 03:02:49.203
2025-07-01 03:02:49.208 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:49.212 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:49.216
2025-07-01 03:02:49.221 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:49.226 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:49.230 alo = 259, ahi = 1101
2025-07-01 03:02:49.235 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:49.240 blo = 259, bhi = 1101
2025-07-01 03:02:49.244
2025-07-01 03:02:49.249 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:49.253 r"""
2025-07-01 03:02:49.258 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:49.262 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:49.266 synch point, and intraline difference marking is done on the
2025-07-01 03:02:49.271 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:49.275
2025-07-01 03:02:49.280 Example:
2025-07-01 03:02:49.285
2025-07-01 03:02:49.289 >>> d = Differ()
2025-07-01 03:02:49.294 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:49.299 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:49.303 >>> print(''.join(results), end="")
2025-07-01 03:02:49.308 - abcDefghiJkl
2025-07-01 03:02:49.317 + abcdefGhijkl
2025-07-01 03:02:49.326 """
2025-07-01 03:02:49.330
2025-07-01 03:02:49.335 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:49.340 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:49.344 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:49.349 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:49.353 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:49.358
2025-07-01 03:02:49.362 # search for the pair that matches best without being identical
2025-07-01 03:02:49.367 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:49.372 # on junk -- unless we have to)
2025-07-01 03:02:49.377 for j in range(blo, bhi):
2025-07-01 03:02:49.381 bj = b[j]
2025-07-01 03:02:49.385 cruncher.set_seq2(bj)
2025-07-01 03:02:49.390 for i in range(alo, ahi):
2025-07-01 03:02:49.394 ai = a[i]
2025-07-01 03:02:49.398 if ai == bj:
2025-07-01 03:02:49.403 if eqi is None:
2025-07-01 03:02:49.407 eqi, eqj = i, j
2025-07-01 03:02:49.412 continue
2025-07-01 03:02:49.416 cruncher.set_seq1(ai)
2025-07-01 03:02:49.421 # computing similarity is expensive, so use the quick
2025-07-01 03:02:49.425 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:49.431 # compares by a factor of 3.
2025-07-01 03:02:49.436 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:49.441 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:49.445 # of the computation is cached by cruncher
2025-07-01 03:02:49.450 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:49.454 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:49.459 cruncher.ratio() > best_ratio:
2025-07-01 03:02:49.464 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:49.469 if best_ratio < cutoff:
2025-07-01 03:02:49.474 # no non-identical "pretty close" pair
2025-07-01 03:02:49.478 if eqi is None:
2025-07-01 03:02:49.482 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:49.487 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:49.491 return
2025-07-01 03:02:49.496 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:49.500 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:49.505 else:
2025-07-01 03:02:49.509 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:49.514 eqi = None
2025-07-01 03:02:49.518
2025-07-01 03:02:49.522 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:49.527 # identical
2025-07-01 03:02:49.531
2025-07-01 03:02:49.535 # pump out diffs from before the synch point
2025-07-01 03:02:49.540 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:49.545
2025-07-01 03:02:49.549 # do intraline marking on the synch pair
2025-07-01 03:02:49.554 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:49.558 if eqi is None:
2025-07-01 03:02:49.562 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:49.567 atags = btags = ""
2025-07-01 03:02:49.571 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:49.576 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:49.581 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:49.586 if tag == 'replace':
2025-07-01 03:02:49.590 atags += '^' * la
2025-07-01 03:02:49.595 btags += '^' * lb
2025-07-01 03:02:49.599 elif tag == 'delete':
2025-07-01 03:02:49.603 atags += '-' * la
2025-07-01 03:02:49.608 elif tag == 'insert':
2025-07-01 03:02:49.612 btags += '+' * lb
2025-07-01 03:02:49.617 elif tag == 'equal':
2025-07-01 03:02:49.621 atags += ' ' * la
2025-07-01 03:02:49.627 btags += ' ' * lb
2025-07-01 03:02:49.633 else:
2025-07-01 03:02:49.639 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:49.644 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:49.650 else:
2025-07-01 03:02:49.655 # the synch pair is identical
2025-07-01 03:02:49.660 yield '  ' + aelt
2025-07-01 03:02:49.666
2025-07-01 03:02:49.670 # pump out diffs from after the synch point
2025-07-01 03:02:49.675 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:49.679
2025-07-01 03:02:49.684 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:49.688 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:49.693
2025-07-01 03:02:49.697 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:49.702 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:49.706 alo = 260, ahi = 1101
2025-07-01 03:02:49.711 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:49.716 blo = 260, bhi = 1101
2025-07-01 03:02:49.721
2025-07-01 03:02:49.726 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:49.731 g = []
2025-07-01 03:02:49.735 if alo < ahi:
2025-07-01 03:02:49.739 if blo < bhi:
2025-07-01 03:02:49.744 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:49.748 else:
2025-07-01 03:02:49.753 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:49.757 elif blo < bhi:
2025-07-01 03:02:49.761 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:49.766
2025-07-01 03:02:49.770 >       yield from g
2025-07-01 03:02:49.775
2025-07-01 03:02:49.780 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:49.785 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:49.789
2025-07-01 03:02:49.793 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:49.798 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:49.802 alo = 260, ahi = 1101
2025-07-01 03:02:49.807 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:49.811 blo = 260, bhi = 1101
2025-07-01 03:02:49.815
2025-07-01 03:02:49.820 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:49.824 r"""
2025-07-01 03:02:49.829 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:49.833 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:49.837 synch point, and intraline difference marking is done on the
2025-07-01 03:02:49.842 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:49.846
2025-07-01 03:02:49.850 Example:
2025-07-01 03:02:49.855
2025-07-01 03:02:49.860 >>> d = Differ()
2025-07-01 03:02:49.865 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:49.869 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:49.874 >>> print(''.join(results), end="")
2025-07-01 03:02:49.878 - abcDefghiJkl
2025-07-01 03:02:49.887 + abcdefGhijkl
2025-07-01 03:02:49.897 """
2025-07-01 03:02:49.901
2025-07-01 03:02:49.905 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:49.910 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:49.914 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:49.919 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:49.923 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:49.928
2025-07-01 03:02:49.933 # search for the pair that matches best without being identical
2025-07-01 03:02:49.937 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:49.942 # on junk -- unless we have to)
2025-07-01 03:02:49.946 for j in range(blo, bhi):
2025-07-01 03:02:49.951 bj = b[j]
2025-07-01 03:02:49.955 cruncher.set_seq2(bj)
2025-07-01 03:02:49.960 for i in range(alo, ahi):
2025-07-01 03:02:49.965 ai = a[i]
2025-07-01 03:02:49.970 if ai == bj:
2025-07-01 03:02:49.976 if eqi is None:
2025-07-01 03:02:49.980 eqi, eqj = i, j
2025-07-01 03:02:49.985 continue
2025-07-01 03:02:49.989 cruncher.set_seq1(ai)
2025-07-01 03:02:49.994 # computing similarity is expensive, so use the quick
2025-07-01 03:02:49.999 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:50.004 # compares by a factor of 3.
2025-07-01 03:02:50.008 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:50.013 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:50.019 # of the computation is cached by cruncher
2025-07-01 03:02:50.025 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:50.030 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:50.034 cruncher.ratio() > best_ratio:
2025-07-01 03:02:50.039 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:50.044 if best_ratio < cutoff:
2025-07-01 03:02:50.049 # no non-identical "pretty close" pair
2025-07-01 03:02:50.054 if eqi is None:
2025-07-01 03:02:50.058 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:50.063 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:50.067 return
2025-07-01 03:02:50.072 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:50.077 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:50.081 else:
2025-07-01 03:02:50.087 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:50.093 eqi = None
2025-07-01 03:02:50.100
2025-07-01 03:02:50.106 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:50.112 # identical
2025-07-01 03:02:50.118
2025-07-01 03:02:50.125 # pump out diffs from before the synch point
2025-07-01 03:02:50.130 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:50.134
2025-07-01 03:02:50.140 # do intraline marking on the synch pair
2025-07-01 03:02:50.146 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:50.151 if eqi is None:
2025-07-01 03:02:50.157 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:50.163 atags = btags = ""
2025-07-01 03:02:50.169 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:50.176 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:50.183 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:50.190 if tag == 'replace':
2025-07-01 03:02:50.197 atags += '^' * la
2025-07-01 03:02:50.203 btags += '^' * lb
2025-07-01 03:02:50.209 elif tag == 'delete':
2025-07-01 03:02:50.214 atags += '-' * la
2025-07-01 03:02:50.218 elif tag == 'insert':
2025-07-01 03:02:50.223 btags += '+' * lb
2025-07-01 03:02:50.227 elif tag == 'equal':
2025-07-01 03:02:50.231 atags += ' ' * la
2025-07-01 03:02:50.235 btags += ' ' * lb
2025-07-01 03:02:50.240 else:
2025-07-01 03:02:50.244 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:50.248 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:50.253 else:
2025-07-01 03:02:50.257 # the synch pair is identical
2025-07-01 03:02:50.261 yield '  ' + aelt
2025-07-01 03:02:50.265
2025-07-01 03:02:50.270 # pump out diffs from after the synch point
2025-07-01 03:02:50.274 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:50.278
2025-07-01 03:02:50.283 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:50.287 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:50.291
2025-07-01 03:02:50.296 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:50.300 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:50.305 alo = 261, ahi = 1101
2025-07-01 03:02:50.309 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:50.313 blo = 261, bhi = 1101
2025-07-01 03:02:50.318
2025-07-01 03:02:50.322 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:50.326 g = []
2025-07-01 03:02:50.331 if alo < ahi:
2025-07-01 03:02:50.335 if blo < bhi:
2025-07-01 03:02:50.339 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:50.343 else:
2025-07-01 03:02:50.348 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:50.352 elif blo < bhi:
2025-07-01 03:02:50.356 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:50.361
2025-07-01 03:02:50.365 >       yield from g
2025-07-01 03:02:50.369
2025-07-01 03:02:50.373 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:50.378 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:50.382
2025-07-01 03:02:50.386 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:50.391 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:50.395 alo = 261, ahi = 1101
2025-07-01 03:02:50.400 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:50.404 blo = 261, bhi = 1101
2025-07-01 03:02:50.408
2025-07-01 03:02:50.413 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:50.417 r"""
2025-07-01 03:02:50.421 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:50.426 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:50.431 synch point, and intraline difference marking is done on the
2025-07-01 03:02:50.435 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:50.440
2025-07-01 03:02:50.444 Example:
2025-07-01 03:02:50.448
2025-07-01 03:02:50.452 >>> d = Differ()
2025-07-01 03:02:50.457 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:50.461 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:50.465 >>> print(''.join(results), end="")
2025-07-01 03:02:50.470 - abcDefghiJkl
2025-07-01 03:02:50.478 + abcdefGhijkl
2025-07-01 03:02:50.486 """
2025-07-01 03:02:50.491
2025-07-01 03:02:50.496 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:50.501 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:50.505 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:50.510 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:50.514 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:50.518
2025-07-01 03:02:50.522 # search for the pair that matches best without being identical
2025-07-01 03:02:50.527 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:50.531 # on junk -- unless we have to)
2025-07-01 03:02:50.536 for j in range(blo, bhi):
2025-07-01 03:02:50.540 bj = b[j]
2025-07-01 03:02:50.544 cruncher.set_seq2(bj)
2025-07-01 03:02:50.549 for i in range(alo, ahi):
2025-07-01 03:02:50.553 ai = a[i]
2025-07-01 03:02:50.557 if ai == bj:
2025-07-01 03:02:50.561 if eqi is None:
2025-07-01 03:02:50.565 eqi, eqj = i, j
2025-07-01 03:02:50.570 continue
2025-07-01 03:02:50.574 cruncher.set_seq1(ai)
2025-07-01 03:02:50.578 # computing similarity is expensive, so use the quick
2025-07-01 03:02:50.583 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:50.587 # compares by a factor of 3.
2025-07-01 03:02:50.591 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:50.596 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:50.600 # of the computation is cached by cruncher
2025-07-01 03:02:50.604 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:50.609 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:50.613 cruncher.ratio() > best_ratio:
2025-07-01 03:02:50.617 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:50.622 if best_ratio < cutoff:
2025-07-01 03:02:50.626 # no non-identical "pretty close" pair
2025-07-01 03:02:50.630 if eqi is None:
2025-07-01 03:02:50.634 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:50.639 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:50.644 return
2025-07-01 03:02:50.648 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:50.652 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:50.656 else:
2025-07-01 03:02:50.661 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:50.665 eqi = None
2025-07-01 03:02:50.669
2025-07-01 03:02:50.673 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:50.678 # identical
2025-07-01 03:02:50.682
2025-07-01 03:02:50.686 # pump out diffs from before the synch point
2025-07-01 03:02:50.691 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:50.695
2025-07-01 03:02:50.699 # do intraline marking on the synch pair
2025-07-01 03:02:50.703 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:50.708 if eqi is None:
2025-07-01 03:02:50.713 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:50.717 atags = btags = ""
2025-07-01 03:02:50.721 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:50.726 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:50.730 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:50.734 if tag == 'replace':
2025-07-01 03:02:50.739 atags += '^' * la
2025-07-01 03:02:50.743 btags += '^' * lb
2025-07-01 03:02:50.748 elif tag == 'delete':
2025-07-01 03:02:50.753 atags += '-' * la
2025-07-01 03:02:50.758 elif tag == 'insert':
2025-07-01 03:02:50.762 btags += '+' * lb
2025-07-01 03:02:50.766 elif tag == 'equal':
2025-07-01 03:02:50.771 atags += ' ' * la
2025-07-01 03:02:50.775 btags += ' ' * lb
2025-07-01 03:02:50.779 else:
2025-07-01 03:02:50.784 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:50.788 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:50.792 else:
2025-07-01 03:02:50.797 # the synch pair is identical
2025-07-01 03:02:50.801 yield '  ' + aelt
2025-07-01 03:02:50.805
2025-07-01 03:02:50.809 # pump out diffs from after the synch point
2025-07-01 03:02:50.814 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:50.818
2025-07-01 03:02:50.822 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:50.827 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:50.831
2025-07-01 03:02:50.835 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:50.840 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:50.844 alo = 262, ahi = 1101
2025-07-01 03:02:50.849 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:50.853 blo = 262, bhi = 1101
2025-07-01 03:02:50.857
2025-07-01 03:02:50.861 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:50.866 g = []
2025-07-01 03:02:50.870 if alo < ahi:
2025-07-01 03:02:50.874 if blo < bhi:
2025-07-01 03:02:50.878 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:50.882 else:
2025-07-01 03:02:50.887 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:50.891 elif blo < bhi:
2025-07-01 03:02:50.895 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:50.899
2025-07-01 03:02:50.903 >       yield from g
2025-07-01 03:02:50.908
2025-07-01 03:02:50.912 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:50.917 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:50.921
2025-07-01 03:02:50.925 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:50.930 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:50.934 alo = 262, ahi = 1101
2025-07-01 03:02:50.939 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:50.943 blo = 262, bhi = 1101
2025-07-01 03:02:50.948
2025-07-01 03:02:50.952 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:50.956 r"""
2025-07-01 03:02:50.961 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:50.965 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:50.969 synch point, and intraline difference marking is done on the
2025-07-01 03:02:50.973 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:50.978
2025-07-01 03:02:50.982 Example:
2025-07-01 03:02:50.986
2025-07-01 03:02:50.991 >>> d = Differ()
2025-07-01 03:02:50.995 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:50.999 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:51.004 >>> print(''.join(results), end="")
2025-07-01 03:02:51.008 - abcDefghiJkl
2025-07-01 03:02:51.017 + abcdefGhijkl
2025-07-01 03:02:51.031 """
2025-07-01 03:02:51.041
2025-07-01 03:02:51.045 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:51.050 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:51.055 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:51.059 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:51.063 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:51.067
2025-07-01 03:02:51.072 # search for the pair that matches best without being identical
2025-07-01 03:02:51.076 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:51.081 # on junk -- unless we have to)
2025-07-01 03:02:51.085 for j in range(blo, bhi):
2025-07-01 03:02:51.089 bj = b[j]
2025-07-01 03:02:51.094 cruncher.set_seq2(bj)
2025-07-01 03:02:51.098 for i in range(alo, ahi):
2025-07-01 03:02:51.102 ai = a[i]
2025-07-01 03:02:51.107 if ai == bj:
2025-07-01 03:02:51.111 if eqi is None:
2025-07-01 03:02:51.115 eqi, eqj = i, j
2025-07-01 03:02:51.119 continue
2025-07-01 03:02:51.124 cruncher.set_seq1(ai)
2025-07-01 03:02:51.128 # computing similarity is expensive, so use the quick
2025-07-01 03:02:51.133 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:51.137 # compares by a factor of 3.
2025-07-01 03:02:51.141 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:51.145 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:51.150 # of the computation is cached by cruncher
2025-07-01 03:02:51.155 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:51.159 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:51.163 cruncher.ratio() > best_ratio:
2025-07-01 03:02:51.168 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:51.172 if best_ratio < cutoff:
2025-07-01 03:02:51.176 # no non-identical "pretty close" pair
2025-07-01 03:02:51.180 if eqi is None:
2025-07-01 03:02:51.185 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:51.189 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:51.194 return
2025-07-01 03:02:51.198 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:51.202 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:51.207 else:
2025-07-01 03:02:51.211 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:51.215 eqi = None
2025-07-01 03:02:51.219
2025-07-01 03:02:51.224 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:51.228 # identical
2025-07-01 03:02:51.233
2025-07-01 03:02:51.237 # pump out diffs from before the synch point
2025-07-01 03:02:51.241 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:51.246
2025-07-01 03:02:51.250 # do intraline marking on the synch pair
2025-07-01 03:02:51.254 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:51.258 if eqi is None:
2025-07-01 03:02:51.263 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:51.267 atags = btags = ""
2025-07-01 03:02:51.272 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:51.277 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:51.282 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:51.286 if tag == 'replace':
2025-07-01 03:02:51.290 atags += '^' * la
2025-07-01 03:02:51.295 btags += '^' * lb
2025-07-01 03:02:51.299 elif tag == 'delete':
2025-07-01 03:02:51.303 atags += '-' * la
2025-07-01 03:02:51.308 elif tag == 'insert':
2025-07-01 03:02:51.312 btags += '+' * lb
2025-07-01 03:02:51.316 elif tag == 'equal':
2025-07-01 03:02:51.320 atags += ' ' * la
2025-07-01 03:02:51.325 btags += ' ' * lb
2025-07-01 03:02:51.329 else:
2025-07-01 03:02:51.333 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:51.337 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:51.341 else:
2025-07-01 03:02:51.346 # the synch pair is identical
2025-07-01 03:02:51.350 yield '  ' + aelt
2025-07-01 03:02:51.354
2025-07-01 03:02:51.359 # pump out diffs from after the synch point
2025-07-01 03:02:51.363 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:51.370
2025-07-01 03:02:51.376 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:51.381 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:51.385
2025-07-01 03:02:51.390 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:51.395 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:51.399 alo = 263, ahi = 1101
2025-07-01 03:02:51.404 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:51.408 blo = 263, bhi = 1101
2025-07-01 03:02:51.412
2025-07-01 03:02:51.417 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:51.421 g = []
2025-07-01 03:02:51.425 if alo < ahi:
2025-07-01 03:02:51.430 if blo < bhi:
2025-07-01 03:02:51.434 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:51.438 else:
2025-07-01 03:02:51.443 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:51.447 elif blo < bhi:
2025-07-01 03:02:51.451 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:51.456
2025-07-01 03:02:51.460 >       yield from g
2025-07-01 03:02:51.464
2025-07-01 03:02:51.468 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:51.474 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:51.483
2025-07-01 03:02:51.487 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:51.492 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:51.497 alo = 263, ahi = 1101
2025-07-01 03:02:51.501 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:51.506 blo = 263, bhi = 1101
2025-07-01 03:02:51.510
2025-07-01 03:02:51.515 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:51.521 r"""
2025-07-01 03:02:51.526 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:51.530 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:51.534 synch point, and intraline difference marking is done on the
2025-07-01 03:02:51.539 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:51.543
2025-07-01 03:02:51.547 Example:
2025-07-01 03:02:51.551
2025-07-01 03:02:51.556 >>> d = Differ()
2025-07-01 03:02:51.560 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:51.565 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:51.569 >>> print(''.join(results), end="")
2025-07-01 03:02:51.573 - abcDefghiJkl
2025-07-01 03:02:51.582 + abcdefGhijkl
2025-07-01 03:02:51.591 """
2025-07-01 03:02:51.595
2025-07-01 03:02:51.599 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:51.603 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:51.608 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:51.612 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:51.617 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:51.621
2025-07-01 03:02:51.625 # search for the pair that matches best without being identical
2025-07-01 03:02:51.630 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:51.634 # on junk -- unless we have to)
2025-07-01 03:02:51.638 for j in range(blo, bhi):
2025-07-01 03:02:51.642 bj = b[j]
2025-07-01 03:02:51.647 cruncher.set_seq2(bj)
2025-07-01 03:02:51.651 for i in range(alo, ahi):
2025-07-01 03:02:51.656 ai = a[i]
2025-07-01 03:02:51.660 if ai == bj:
2025-07-01 03:02:51.664 if eqi is None:
2025-07-01 03:02:51.668 eqi, eqj = i, j
2025-07-01 03:02:51.673 continue
2025-07-01 03:02:51.677 cruncher.set_seq1(ai)
2025-07-01 03:02:51.682 # computing similarity is expensive, so use the quick
2025-07-01 03:02:51.686 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:51.691 # compares by a factor of 3.
2025-07-01 03:02:51.695 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:51.699 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:51.704 # of the computation is cached by cruncher
2025-07-01 03:02:51.708 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:51.712 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:51.717 cruncher.ratio() > best_ratio:
2025-07-01 03:02:51.721 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:51.725 if best_ratio < cutoff:
2025-07-01 03:02:51.730 # no non-identical "pretty close" pair
2025-07-01 03:02:51.734 if eqi is None:
2025-07-01 03:02:51.738 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:51.742 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:51.747 return
2025-07-01 03:02:51.751 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:51.755 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:51.760 else:
2025-07-01 03:02:51.764 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:51.768 eqi = None
2025-07-01 03:02:51.772
2025-07-01 03:02:51.776 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:51.781 # identical
2025-07-01 03:02:51.785
2025-07-01 03:02:51.789 # pump out diffs from before the synch point
2025-07-01 03:02:51.793 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:51.798
2025-07-01 03:02:51.802 # do intraline marking on the synch pair
2025-07-01 03:02:51.806 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:51.810 if eqi is None:
2025-07-01 03:02:51.814 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:51.819 atags = btags = ""
2025-07-01 03:02:51.823 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:51.827 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:51.832 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:51.836 if tag == 'replace':
2025-07-01 03:02:51.840 atags += '^' * la
2025-07-01 03:02:51.845 btags += '^' * lb
2025-07-01 03:02:51.849 elif tag == 'delete':
2025-07-01 03:02:51.853 atags += '-' * la
2025-07-01 03:02:51.858 elif tag == 'insert':
2025-07-01 03:02:51.862 btags += '+' * lb
2025-07-01 03:02:51.866 elif tag == 'equal':
2025-07-01 03:02:51.871 atags += ' ' * la
2025-07-01 03:02:51.875 btags += ' ' * lb
2025-07-01 03:02:51.879 else:
2025-07-01 03:02:51.884 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:51.888 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:51.892 else:
2025-07-01 03:02:51.897 # the synch pair is identical
2025-07-01 03:02:51.901 yield '  ' + aelt
2025-07-01 03:02:51.905
2025-07-01 03:02:51.909 # pump out diffs from after the synch point
2025-07-01 03:02:51.913 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:51.918
2025-07-01 03:02:51.922 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:51.926 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:51.931
2025-07-01 03:02:51.935 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:51.939 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:51.944 alo = 264, ahi = 1101
2025-07-01 03:02:51.949 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:51.953 blo = 264, bhi = 1101
2025-07-01 03:02:51.957
2025-07-01 03:02:51.961 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:51.965 g = []
2025-07-01 03:02:51.970 if alo < ahi:
2025-07-01 03:02:51.974 if blo < bhi:
2025-07-01 03:02:51.978 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:51.983 else:
2025-07-01 03:02:51.987 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:51.991 elif blo < bhi:
2025-07-01 03:02:51.996 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:52.000
2025-07-01 03:02:52.004 >       yield from g
2025-07-01 03:02:52.009
2025-07-01 03:02:52.013 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:52.017 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:52.022
2025-07-01 03:02:52.026 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:52.030 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:52.035 alo = 264, ahi = 1101
2025-07-01 03:02:52.040 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:52.044 blo = 264, bhi = 1101
2025-07-01 03:02:52.049
2025-07-01 03:02:52.053 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:52.057 r"""
2025-07-01 03:02:52.062 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:52.066 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:52.071 synch point, and intraline difference marking is done on the
2025-07-01 03:02:52.076 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:52.080
2025-07-01 03:02:52.085 Example:
2025-07-01 03:02:52.090
2025-07-01 03:02:52.094 >>> d = Differ()
2025-07-01 03:02:52.098 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:52.103 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:52.107 >>> print(''.join(results), end="")
2025-07-01 03:02:52.115 - abcDefghiJkl
2025-07-01 03:02:52.125 + abcdefGhijkl
2025-07-01 03:02:52.134 """
2025-07-01 03:02:52.138
2025-07-01 03:02:52.144 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:52.149 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:52.153 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:52.158 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:52.162 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:52.166
2025-07-01 03:02:52.171 # search for the pair that matches best without being identical
2025-07-01 03:02:52.176 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:52.180 # on junk -- unless we have to)
2025-07-01 03:02:52.185 for j in range(blo, bhi):
2025-07-01 03:02:52.190 bj = b[j]
2025-07-01 03:02:52.194 cruncher.set_seq2(bj)
2025-07-01 03:02:52.199 for i in range(alo, ahi):
2025-07-01 03:02:52.203 ai = a[i]
2025-07-01 03:02:52.208 if ai == bj:
2025-07-01 03:02:52.212 if eqi is None:
2025-07-01 03:02:52.217 eqi, eqj = i, j
2025-07-01 03:02:52.223 continue
2025-07-01 03:02:52.229 cruncher.set_seq1(ai)
2025-07-01 03:02:52.236 # computing similarity is expensive, so use the quick
2025-07-01 03:02:52.242 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:52.247 # compares by a factor of 3.
2025-07-01 03:02:52.253 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:52.259 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:52.265 # of the computation is cached by cruncher
2025-07-01 03:02:52.271 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:52.277 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:52.283 cruncher.ratio() > best_ratio:
2025-07-01 03:02:52.289 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:52.294 if best_ratio < cutoff:
2025-07-01 03:02:52.301 # no non-identical "pretty close" pair
2025-07-01 03:02:52.306 if eqi is None:
2025-07-01 03:02:52.312 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:52.318 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:52.324 return
2025-07-01 03:02:52.331 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:52.337 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:52.342 else:
2025-07-01 03:02:52.349 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:52.354 eqi = None
2025-07-01 03:02:52.360
2025-07-01 03:02:52.366 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:52.372 # identical
2025-07-01 03:02:52.378
2025-07-01 03:02:52.384 # pump out diffs from before the synch point
2025-07-01 03:02:52.389 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:52.393
2025-07-01 03:02:52.397 # do intraline marking on the synch pair
2025-07-01 03:02:52.402 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:52.407 if eqi is None:
2025-07-01 03:02:52.411 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:52.415 atags = btags = ""
2025-07-01 03:02:52.420 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:52.424 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:52.429 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:52.433 if tag == 'replace':
2025-07-01 03:02:52.437 atags += '^' * la
2025-07-01 03:02:52.442 btags += '^' * lb
2025-07-01 03:02:52.446 elif tag == 'delete':
2025-07-01 03:02:52.451 atags += '-' * la
2025-07-01 03:02:52.456 elif tag == 'insert':
2025-07-01 03:02:52.460 btags += '+' * lb
2025-07-01 03:02:52.465 elif tag == 'equal':
2025-07-01 03:02:52.469 atags += ' ' * la
2025-07-01 03:02:52.474 btags += ' ' * lb
2025-07-01 03:02:52.478 else:
2025-07-01 03:02:52.482 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:52.487 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:52.491 else:
2025-07-01 03:02:52.495 # the synch pair is identical
2025-07-01 03:02:52.500 yield '  ' + aelt
2025-07-01 03:02:52.504
2025-07-01 03:02:52.508 # pump out diffs from after the synch point
2025-07-01 03:02:52.513 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:52.517
2025-07-01 03:02:52.521 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:52.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:52.530
2025-07-01 03:02:52.534 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:52.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:52.546 alo = 265, ahi = 1101
2025-07-01 03:02:52.553 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:52.558 blo = 265, bhi = 1101
2025-07-01 03:02:52.562
2025-07-01 03:02:52.566 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:52.571 g = []
2025-07-01 03:02:52.575 if alo < ahi:
2025-07-01 03:02:52.579 if blo < bhi:
2025-07-01 03:02:52.583 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:52.588 else:
2025-07-01 03:02:52.592 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:52.597 elif blo < bhi:
2025-07-01 03:02:52.602 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:52.606
2025-07-01 03:02:52.610 >       yield from g
2025-07-01 03:02:52.614
2025-07-01 03:02:52.619 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:52.623 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:52.627
2025-07-01 03:02:52.632 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:52.636 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:52.641 alo = 265, ahi = 1101
2025-07-01 03:02:52.645 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:52.649 blo = 265, bhi = 1101
2025-07-01 03:02:52.654
2025-07-01 03:02:52.658 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:52.662 r"""
2025-07-01 03:02:52.667 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:52.671 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:52.675 synch point, and intraline difference marking is done on the
2025-07-01 03:02:52.680 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:52.684
2025-07-01 03:02:52.688 Example:
2025-07-01 03:02:52.692
2025-07-01 03:02:52.696 >>> d = Differ()
2025-07-01 03:02:52.701 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:52.705 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:52.709 >>> print(''.join(results), end="")
2025-07-01 03:02:52.714 - abcDefghiJkl
2025-07-01 03:02:52.723 + abcdefGhijkl
2025-07-01 03:02:52.731 """
2025-07-01 03:02:52.736
2025-07-01 03:02:52.740 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:52.744 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:52.748 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:52.753 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:52.757 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:52.761
2025-07-01 03:02:52.765 # search for the pair that matches best without being identical
2025-07-01 03:02:52.770 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:52.774 # on junk -- unless we have to)
2025-07-01 03:02:52.778 for j in range(blo, bhi):
2025-07-01 03:02:52.782 bj = b[j]
2025-07-01 03:02:52.786 cruncher.set_seq2(bj)
2025-07-01 03:02:52.791 for i in range(alo, ahi):
2025-07-01 03:02:52.795 ai = a[i]
2025-07-01 03:02:52.799 if ai == bj:
2025-07-01 03:02:52.803 if eqi is None:
2025-07-01 03:02:52.808 eqi, eqj = i, j
2025-07-01 03:02:52.812 continue
2025-07-01 03:02:52.816 cruncher.set_seq1(ai)
2025-07-01 03:02:52.821 # computing similarity is expensive, so use the quick
2025-07-01 03:02:52.825 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:52.829 # compares by a factor of 3.
2025-07-01 03:02:52.834 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:52.839 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:52.844 # of the computation is cached by cruncher
2025-07-01 03:02:52.848 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:52.853 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:52.857 cruncher.ratio() > best_ratio:
2025-07-01 03:02:52.862 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:52.866 if best_ratio < cutoff:
2025-07-01 03:02:52.870 # no non-identical "pretty close" pair
2025-07-01 03:02:52.875 if eqi is None:
2025-07-01 03:02:52.879 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:52.883 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:52.888 return
2025-07-01 03:02:52.892 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:52.897 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:52.901 else:
2025-07-01 03:02:52.905 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:52.910 eqi = None
2025-07-01 03:02:52.914
2025-07-01 03:02:52.918 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:52.923 # identical
2025-07-01 03:02:52.927
2025-07-01 03:02:52.932 # pump out diffs from before the synch point
2025-07-01 03:02:52.937 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:52.944
2025-07-01 03:02:52.955 # do intraline marking on the synch pair
2025-07-01 03:02:52.966 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:52.973 if eqi is None:
2025-07-01 03:02:52.980 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:52.988 atags = btags = ""
2025-07-01 03:02:52.995 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:53.003 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:53.013 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:53.023 if tag == 'replace':
2025-07-01 03:02:53.032 atags += '^' * la
2025-07-01 03:02:53.038 btags += '^' * lb
2025-07-01 03:02:53.044 elif tag == 'delete':
2025-07-01 03:02:53.049 atags += '-' * la
2025-07-01 03:02:53.054 elif tag == 'insert':
2025-07-01 03:02:53.059 btags += '+' * lb
2025-07-01 03:02:53.065 elif tag == 'equal':
2025-07-01 03:02:53.079 atags += ' ' * la
2025-07-01 03:02:53.091 btags += ' ' * lb
2025-07-01 03:02:53.101 else:
2025-07-01 03:02:53.109 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:53.116 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:53.121 else:
2025-07-01 03:02:53.127 # the synch pair is identical
2025-07-01 03:02:53.132 yield '  ' + aelt
2025-07-01 03:02:53.139
2025-07-01 03:02:53.147 # pump out diffs from after the synch point
2025-07-01 03:02:53.153 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:53.158
2025-07-01 03:02:53.164 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:53.174 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:53.180
2025-07-01 03:02:53.186 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:53.192 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:53.197 alo = 268, ahi = 1101
2025-07-01 03:02:53.208 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:53.217 blo = 268, bhi = 1101
2025-07-01 03:02:53.225
2025-07-01 03:02:53.238 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:53.246 g = []
2025-07-01 03:02:53.253 if alo < ahi:
2025-07-01 03:02:53.266 if blo < bhi:
2025-07-01 03:02:53.271 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:53.278 else:
2025-07-01 03:02:53.287 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:53.293 elif blo < bhi:
2025-07-01 03:02:53.302 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:53.312
2025-07-01 03:02:53.319 >       yield from g
2025-07-01 03:02:53.326
2025-07-01 03:02:53.335 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:53.342 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:53.348
2025-07-01 03:02:53.354 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:53.360 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:53.366 alo = 268, ahi = 1101
2025-07-01 03:02:53.372 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:53.378 blo = 268, bhi = 1101
2025-07-01 03:02:53.384
2025-07-01 03:02:53.391 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:53.400 r"""
2025-07-01 03:02:53.406 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:53.411 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:53.417 synch point, and intraline difference marking is done on the
2025-07-01 03:02:53.422 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:53.433
2025-07-01 03:02:53.449 Example:
2025-07-01 03:02:53.461
2025-07-01 03:02:53.472 >>> d = Differ()
2025-07-01 03:02:53.485 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:53.496 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:53.507 >>> print(''.join(results), end="")
2025-07-01 03:02:53.515 - abcDefghiJkl
2025-07-01 03:02:53.534 + abcdefGhijkl
2025-07-01 03:02:53.555 """
2025-07-01 03:02:53.566
2025-07-01 03:02:53.572 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:53.578 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:53.590 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:53.598 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:53.605 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:53.611
2025-07-01 03:02:53.617 # search for the pair that matches best without being identical
2025-07-01 03:02:53.623 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:53.629 # on junk -- unless we have to)
2025-07-01 03:02:53.635 for j in range(blo, bhi):
2025-07-01 03:02:53.640 bj = b[j]
2025-07-01 03:02:53.646 cruncher.set_seq2(bj)
2025-07-01 03:02:53.652 for i in range(alo, ahi):
2025-07-01 03:02:53.657 ai = a[i]
2025-07-01 03:02:53.662 if ai == bj:
2025-07-01 03:02:53.667 if eqi is None:
2025-07-01 03:02:53.673 eqi, eqj = i, j
2025-07-01 03:02:53.678 continue
2025-07-01 03:02:53.684 cruncher.set_seq1(ai)
2025-07-01 03:02:53.689 # computing similarity is expensive, so use the quick
2025-07-01 03:02:53.695 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:53.701 # compares by a factor of 3.
2025-07-01 03:02:53.709 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:53.715 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:53.721 # of the computation is cached by cruncher
2025-07-01 03:02:53.727 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:53.733 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:53.739 cruncher.ratio() > best_ratio:
2025-07-01 03:02:53.746 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:53.757 if best_ratio < cutoff:
2025-07-01 03:02:53.764 # no non-identical "pretty close" pair
2025-07-01 03:02:53.771 if eqi is None:
2025-07-01 03:02:53.778 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:53.786 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:53.793 return
2025-07-01 03:02:53.800 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:53.808 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:53.817 else:
2025-07-01 03:02:53.823 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:53.829 eqi = None
2025-07-01 03:02:53.834
2025-07-01 03:02:53.841 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:53.846 # identical
2025-07-01 03:02:53.853
2025-07-01 03:02:53.859 # pump out diffs from before the synch point
2025-07-01 03:02:53.865 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:53.870
2025-07-01 03:02:53.876 # do intraline marking on the synch pair
2025-07-01 03:02:53.880 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:53.885 if eqi is None:
2025-07-01 03:02:53.890 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:53.895 atags = btags = ""
2025-07-01 03:02:53.900 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:53.906 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:53.912 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:53.917 if tag == 'replace':
2025-07-01 03:02:53.923 atags += '^' * la
2025-07-01 03:02:53.929 btags += '^' * lb
2025-07-01 03:02:53.934 elif tag == 'delete':
2025-07-01 03:02:53.940 atags += '-' * la
2025-07-01 03:02:53.946 elif tag == 'insert':
2025-07-01 03:02:53.952 btags += '+' * lb
2025-07-01 03:02:53.959 elif tag == 'equal':
2025-07-01 03:02:53.967 atags += ' ' * la
2025-07-01 03:02:53.973 btags += ' ' * lb
2025-07-01 03:02:53.979 else:
2025-07-01 03:02:53.984 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:53.990 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:53.995 else:
2025-07-01 03:02:54.001 # the synch pair is identical
2025-07-01 03:02:54.009 yield '  ' + aelt
2025-07-01 03:02:54.021
2025-07-01 03:02:54.029 # pump out diffs from after the synch point
2025-07-01 03:02:54.035 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:54.041
2025-07-01 03:02:54.046 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:54.053 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:54.058
2025-07-01 03:02:54.065 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:54.072 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:54.078 alo = 269, ahi = 1101
2025-07-01 03:02:54.084 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:54.090 blo = 269, bhi = 1101
2025-07-01 03:02:54.097
2025-07-01 03:02:54.103 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:54.109 g = []
2025-07-01 03:02:54.115 if alo < ahi:
2025-07-01 03:02:54.121 if blo < bhi:
2025-07-01 03:02:54.129 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:54.136 else:
2025-07-01 03:02:54.144 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:54.150 elif blo < bhi:
2025-07-01 03:02:54.157 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:54.165
2025-07-01 03:02:54.172 >       yield from g
2025-07-01 03:02:54.179
2025-07-01 03:02:54.185 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:54.191 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:54.197
2025-07-01 03:02:54.203 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:54.209 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:54.215 alo = 269, ahi = 1101
2025-07-01 03:02:54.222 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:54.227 blo = 269, bhi = 1101
2025-07-01 03:02:54.233
2025-07-01 03:02:54.239 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:54.246 r"""
2025-07-01 03:02:54.252 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:54.258 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:54.264 synch point, and intraline difference marking is done on the
2025-07-01 03:02:54.270 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:54.275
2025-07-01 03:02:54.286 Example:
2025-07-01 03:02:54.297
2025-07-01 03:02:54.303 >>> d = Differ()
2025-07-01 03:02:54.311 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:54.318 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:54.324 >>> print(''.join(results), end="")
2025-07-01 03:02:54.329 - abcDefghiJkl
2025-07-01 03:02:54.340 + abcdefGhijkl
2025-07-01 03:02:54.353 """
2025-07-01 03:02:54.359
2025-07-01 03:02:54.365 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:54.372 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:54.378 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:54.384 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:54.390 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:54.395
2025-07-01 03:02:54.401 # search for the pair that matches best without being identical
2025-07-01 03:02:54.406 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:54.411 # on junk -- unless we have to)
2025-07-01 03:02:54.416 for j in range(blo, bhi):
2025-07-01 03:02:54.420 bj = b[j]
2025-07-01 03:02:54.426 cruncher.set_seq2(bj)
2025-07-01 03:02:54.432 for i in range(alo, ahi):
2025-07-01 03:02:54.437 ai = a[i]
2025-07-01 03:02:54.443 if ai == bj:
2025-07-01 03:02:54.448 if eqi is None:
2025-07-01 03:02:54.454 eqi, eqj = i, j
2025-07-01 03:02:54.459 continue
2025-07-01 03:02:54.465 cruncher.set_seq1(ai)
2025-07-01 03:02:54.470 # computing similarity is expensive, so use the quick
2025-07-01 03:02:54.476 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:54.482 # compares by a factor of 3.
2025-07-01 03:02:54.488 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:54.494 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:54.500 # of the computation is cached by cruncher
2025-07-01 03:02:54.506 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:54.511 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:54.516 cruncher.ratio() > best_ratio:
2025-07-01 03:02:54.522 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:54.528 if best_ratio < cutoff:
2025-07-01 03:02:54.534 # no non-identical "pretty close" pair
2025-07-01 03:02:54.540 if eqi is None:
2025-07-01 03:02:54.545 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:54.551 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:54.557 return
2025-07-01 03:02:54.563 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:54.569 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:54.575 else:
2025-07-01 03:02:54.581 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:54.587 eqi = None
2025-07-01 03:02:54.593
2025-07-01 03:02:54.602 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:54.613 # identical
2025-07-01 03:02:54.619
2025-07-01 03:02:54.626 # pump out diffs from before the synch point
2025-07-01 03:02:54.632 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:54.638
2025-07-01 03:02:54.644 # do intraline marking on the synch pair
2025-07-01 03:02:54.651 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:54.659 if eqi is None:
2025-07-01 03:02:54.665 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:54.670 atags = btags = ""
2025-07-01 03:02:54.675 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:54.680 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:54.685 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:54.690 if tag == 'replace':
2025-07-01 03:02:54.696 atags += '^' * la
2025-07-01 03:02:54.701 btags += '^' * lb
2025-07-01 03:02:54.707 elif tag == 'delete':
2025-07-01 03:02:54.713 atags += '-' * la
2025-07-01 03:02:54.719 elif tag == 'insert':
2025-07-01 03:02:54.724 btags += '+' * lb
2025-07-01 03:02:54.730 elif tag == 'equal':
2025-07-01 03:02:54.735 atags += ' ' * la
2025-07-01 03:02:54.740 btags += ' ' * lb
2025-07-01 03:02:54.745 else:
2025-07-01 03:02:54.750 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:54.755 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:54.761 else:
2025-07-01 03:02:54.769 # the synch pair is identical
2025-07-01 03:02:54.777 yield '  ' + aelt
2025-07-01 03:02:54.783
2025-07-01 03:02:54.789 # pump out diffs from after the synch point
2025-07-01 03:02:54.795 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:54.801
2025-07-01 03:02:54.807 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:54.813 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:54.819
2025-07-01 03:02:54.825 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:54.831 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:54.836 alo = 270, ahi = 1101
2025-07-01 03:02:54.841 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:54.847 blo = 270, bhi = 1101
2025-07-01 03:02:54.853
2025-07-01 03:02:54.859 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:54.865 g = []
2025-07-01 03:02:54.871 if alo < ahi:
2025-07-01 03:02:54.878 if blo < bhi:
2025-07-01 03:02:54.885 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:54.891 else:
2025-07-01 03:02:54.897 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:54.903 elif blo < bhi:
2025-07-01 03:02:54.910 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:54.916
2025-07-01 03:02:54.922 >       yield from g
2025-07-01 03:02:54.928
2025-07-01 03:02:54.934 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:54.939 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:54.945
2025-07-01 03:02:54.951 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:54.958 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:54.964 alo = 270, ahi = 1101
2025-07-01 03:02:54.970 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:54.976 blo = 270, bhi = 1101
2025-07-01 03:02:54.982
2025-07-01 03:02:54.990 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:55.001 r"""
2025-07-01 03:02:55.009 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:55.016 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:55.022 synch point, and intraline difference marking is done on the
2025-07-01 03:02:55.028 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:55.033
2025-07-01 03:02:55.037 Example:
2025-07-01 03:02:55.042
2025-07-01 03:02:55.047 >>> d = Differ()
2025-07-01 03:02:55.051 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:55.056 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:55.061 >>> print(''.join(results), end="")
2025-07-01 03:02:55.065 - abcDefghiJkl
2025-07-01 03:02:55.074 + abcdefGhijkl
2025-07-01 03:02:55.084 """
2025-07-01 03:02:55.090
2025-07-01 03:02:55.098 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:55.109 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:55.117 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:55.123 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:55.128 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:55.133
2025-07-01 03:02:55.137 # search for the pair that matches best without being identical
2025-07-01 03:02:55.142 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:55.147 # on junk -- unless we have to)
2025-07-01 03:02:55.152 for j in range(blo, bhi):
2025-07-01 03:02:55.157 bj = b[j]
2025-07-01 03:02:55.162 cruncher.set_seq2(bj)
2025-07-01 03:02:55.167 for i in range(alo, ahi):
2025-07-01 03:02:55.173 ai = a[i]
2025-07-01 03:02:55.179 if ai == bj:
2025-07-01 03:02:55.183 if eqi is None:
2025-07-01 03:02:55.188 eqi, eqj = i, j
2025-07-01 03:02:55.194 continue
2025-07-01 03:02:55.199 cruncher.set_seq1(ai)
2025-07-01 03:02:55.204 # computing similarity is expensive, so use the quick
2025-07-01 03:02:55.209 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:55.215 # compares by a factor of 3.
2025-07-01 03:02:55.221 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:55.228 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:55.234 # of the computation is cached by cruncher
2025-07-01 03:02:55.240 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:55.244 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:55.249 cruncher.ratio() > best_ratio:
2025-07-01 03:02:55.254 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:55.258 if best_ratio < cutoff:
2025-07-01 03:02:55.263 # no non-identical "pretty close" pair
2025-07-01 03:02:55.267 if eqi is None:
2025-07-01 03:02:55.272 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:55.276 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:55.280 return
2025-07-01 03:02:55.285 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:55.290 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:55.294 else:
2025-07-01 03:02:55.299 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:55.304 eqi = None
2025-07-01 03:02:55.309
2025-07-01 03:02:55.314 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:55.319 # identical
2025-07-01 03:02:55.327
2025-07-01 03:02:55.336 # pump out diffs from before the synch point
2025-07-01 03:02:55.342 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:55.348
2025-07-01 03:02:55.354 # do intraline marking on the synch pair
2025-07-01 03:02:55.360 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:55.366 if eqi is None:
2025-07-01 03:02:55.372 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:55.378 atags = btags = ""
2025-07-01 03:02:55.383 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:55.389 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:55.395 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:55.401 if tag == 'replace':
2025-07-01 03:02:55.407 atags += '^' * la
2025-07-01 03:02:55.412 btags += '^' * lb
2025-07-01 03:02:55.417 elif tag == 'delete':
2025-07-01 03:02:55.422 atags += '-' * la
2025-07-01 03:02:55.426 elif tag == 'insert':
2025-07-01 03:02:55.431 btags += '+' * lb
2025-07-01 03:02:55.436 elif tag == 'equal':
2025-07-01 03:02:55.441 atags += ' ' * la
2025-07-01 03:02:55.445 btags += ' ' * lb
2025-07-01 03:02:55.450 else:
2025-07-01 03:02:55.454 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:55.459 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:55.463 else:
2025-07-01 03:02:55.468 # the synch pair is identical
2025-07-01 03:02:55.473 yield '  ' + aelt
2025-07-01 03:02:55.477
2025-07-01 03:02:55.483 # pump out diffs from after the synch point
2025-07-01 03:02:55.487 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:55.491
2025-07-01 03:02:55.496 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:55.501 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:55.505
2025-07-01 03:02:55.509 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:55.515 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:55.519 alo = 271, ahi = 1101
2025-07-01 03:02:55.524 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:55.529 blo = 271, bhi = 1101
2025-07-01 03:02:55.534
2025-07-01 03:02:55.538 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:55.542 g = []
2025-07-01 03:02:55.547 if alo < ahi:
2025-07-01 03:02:55.551 if blo < bhi:
2025-07-01 03:02:55.556 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:55.561 else:
2025-07-01 03:02:55.565 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:55.570 elif blo < bhi:
2025-07-01 03:02:55.574 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:55.578
2025-07-01 03:02:55.582 >       yield from g
2025-07-01 03:02:55.587
2025-07-01 03:02:55.592 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:55.596 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:55.601
2025-07-01 03:02:55.608 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:55.615 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:55.621 alo = 271, ahi = 1101
2025-07-01 03:02:55.627 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:55.633 blo = 271, bhi = 1101
2025-07-01 03:02:55.637
2025-07-01 03:02:55.645 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:55.655 r"""
2025-07-01 03:02:55.662 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:55.668 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:55.674 synch point, and intraline difference marking is done on the
2025-07-01 03:02:55.680 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:55.685
2025-07-01 03:02:55.691 Example:
2025-07-01 03:02:55.696
2025-07-01 03:02:55.703 >>> d = Differ()
2025-07-01 03:02:55.712 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:55.717 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:55.721 >>> print(''.join(results), end="")
2025-07-01 03:02:55.731 - abcDefghiJkl
2025-07-01 03:02:55.745 + abcdefGhijkl
2025-07-01 03:02:55.755 """
2025-07-01 03:02:55.759
2025-07-01 03:02:55.764 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:55.769 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:55.773 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:55.778 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:55.782 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:55.786
2025-07-01 03:02:55.791 # search for the pair that matches best without being identical
2025-07-01 03:02:55.797 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:55.803 # on junk -- unless we have to)
2025-07-01 03:02:55.809 for j in range(blo, bhi):
2025-07-01 03:02:55.814 bj = b[j]
2025-07-01 03:02:55.819 cruncher.set_seq2(bj)
2025-07-01 03:02:55.823 for i in range(alo, ahi):
2025-07-01 03:02:55.828 ai = a[i]
2025-07-01 03:02:55.838 if ai == bj:
2025-07-01 03:02:55.846 if eqi is None:
2025-07-01 03:02:55.853 eqi, eqj = i, j
2025-07-01 03:02:55.858 continue
2025-07-01 03:02:55.864 cruncher.set_seq1(ai)
2025-07-01 03:02:55.869 # computing similarity is expensive, so use the quick
2025-07-01 03:02:55.873 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:55.878 # compares by a factor of 3.
2025-07-01 03:02:55.882 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:55.887 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:55.891 # of the computation is cached by cruncher
2025-07-01 03:02:55.896 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:55.900 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:55.905 cruncher.ratio() > best_ratio:
2025-07-01 03:02:55.909 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:55.913 if best_ratio < cutoff:
2025-07-01 03:02:55.918 # no non-identical "pretty close" pair
2025-07-01 03:02:55.922 if eqi is None:
2025-07-01 03:02:55.928 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:55.934 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:55.941 return
2025-07-01 03:02:55.948 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:55.954 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:55.960 else:
2025-07-01 03:02:55.966 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:55.972 eqi = None
2025-07-01 03:02:55.977
2025-07-01 03:02:55.981 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:55.986 # identical
2025-07-01 03:02:55.990
2025-07-01 03:02:55.995 # pump out diffs from before the synch point
2025-07-01 03:02:55.999 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:56.004
2025-07-01 03:02:56.009 # do intraline marking on the synch pair
2025-07-01 03:02:56.013 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:56.018 if eqi is None:
2025-07-01 03:02:56.023 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:56.027 atags = btags = ""
2025-07-01 03:02:56.031 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:56.036 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:56.041 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:56.045 if tag == 'replace':
2025-07-01 03:02:56.049 atags += '^' * la
2025-07-01 03:02:56.054 btags += '^' * lb
2025-07-01 03:02:56.058 elif tag == 'delete':
2025-07-01 03:02:56.063 atags += '-' * la
2025-07-01 03:02:56.067 elif tag == 'insert':
2025-07-01 03:02:56.071 btags += '+' * lb
2025-07-01 03:02:56.076 elif tag == 'equal':
2025-07-01 03:02:56.080 atags += ' ' * la
2025-07-01 03:02:56.085 btags += ' ' * lb
2025-07-01 03:02:56.089 else:
2025-07-01 03:02:56.094 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:56.098 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:56.103 else:
2025-07-01 03:02:56.107 # the synch pair is identical
2025-07-01 03:02:56.111 yield '  ' + aelt
2025-07-01 03:02:56.116
2025-07-01 03:02:56.120 # pump out diffs from after the synch point
2025-07-01 03:02:56.125 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:56.129
2025-07-01 03:02:56.134 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:56.138 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:56.142
2025-07-01 03:02:56.147 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:56.151 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:56.156 alo = 272, ahi = 1101
2025-07-01 03:02:56.161 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:56.165 blo = 272, bhi = 1101
2025-07-01 03:02:56.170
2025-07-01 03:02:56.174 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:56.179 g = []
2025-07-01 03:02:56.183 if alo < ahi:
2025-07-01 03:02:56.188 if blo < bhi:
2025-07-01 03:02:56.193 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:56.197 else:
2025-07-01 03:02:56.202 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:56.207 elif blo < bhi:
2025-07-01 03:02:56.211 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:56.216
2025-07-01 03:02:56.221 >       yield from g
2025-07-01 03:02:56.225
2025-07-01 03:02:56.230 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:56.234 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:56.239
2025-07-01 03:02:56.243 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:56.248 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:56.253 alo = 272, ahi = 1101
2025-07-01 03:02:56.258 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:56.262 blo = 272, bhi = 1101
2025-07-01 03:02:56.266
2025-07-01 03:02:56.271 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:56.275 r"""
2025-07-01 03:02:56.279 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:56.284 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:56.288 synch point, and intraline difference marking is done on the
2025-07-01 03:02:56.293 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:56.297
2025-07-01 03:02:56.304 Example:
2025-07-01 03:02:56.310
2025-07-01 03:02:56.315 >>> d = Differ()
2025-07-01 03:02:56.321 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:56.326 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:56.331 >>> print(''.join(results), end="")
2025-07-01 03:02:56.337 - abcDefghiJkl
2025-07-01 03:02:56.346 + abcdefGhijkl
2025-07-01 03:02:56.356 """
2025-07-01 03:02:56.360
2025-07-01 03:02:56.365 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:56.370 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:56.374 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:56.379 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:56.384 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:56.388
2025-07-01 03:02:56.394 # search for the pair that matches best without being identical
2025-07-01 03:02:56.401 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:56.407 # on junk -- unless we have to)
2025-07-01 03:02:56.413 for j in range(blo, bhi):
2025-07-01 03:02:56.418 bj = b[j]
2025-07-01 03:02:56.423 cruncher.set_seq2(bj)
2025-07-01 03:02:56.427 for i in range(alo, ahi):
2025-07-01 03:02:56.432 ai = a[i]
2025-07-01 03:02:56.438 if ai == bj:
2025-07-01 03:02:56.444 if eqi is None:
2025-07-01 03:02:56.450 eqi, eqj = i, j
2025-07-01 03:02:56.456 continue
2025-07-01 03:02:56.461 cruncher.set_seq1(ai)
2025-07-01 03:02:56.467 # computing similarity is expensive, so use the quick
2025-07-01 03:02:56.473 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:56.479 # compares by a factor of 3.
2025-07-01 03:02:56.484 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:56.490 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:56.496 # of the computation is cached by cruncher
2025-07-01 03:02:56.502 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:56.508 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:56.514 cruncher.ratio() > best_ratio:
2025-07-01 03:02:56.520 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:56.525 if best_ratio < cutoff:
2025-07-01 03:02:56.531 # no non-identical "pretty close" pair
2025-07-01 03:02:56.538 if eqi is None:
2025-07-01 03:02:56.544 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:56.549 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:56.554 return
2025-07-01 03:02:56.559 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:56.564 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:56.568 else:
2025-07-01 03:02:56.573 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:56.578 eqi = None
2025-07-01 03:02:56.583
2025-07-01 03:02:56.588 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:56.592 # identical
2025-07-01 03:02:56.597
2025-07-01 03:02:56.602 # pump out diffs from before the synch point
2025-07-01 03:02:56.607 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:56.612
2025-07-01 03:02:56.617 # do intraline marking on the synch pair
2025-07-01 03:02:56.622 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:56.626 if eqi is None:
2025-07-01 03:02:56.631 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:56.636 atags = btags = ""
2025-07-01 03:02:56.642 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:56.647 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:56.653 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:56.657 if tag == 'replace':
2025-07-01 03:02:56.662 atags += '^' * la
2025-07-01 03:02:56.667 btags += '^' * lb
2025-07-01 03:02:56.671 elif tag == 'delete':
2025-07-01 03:02:56.676 atags += '-' * la
2025-07-01 03:02:56.681 elif tag == 'insert':
2025-07-01 03:02:56.685 btags += '+' * lb
2025-07-01 03:02:56.690 elif tag == 'equal':
2025-07-01 03:02:56.695 atags += ' ' * la
2025-07-01 03:02:56.700 btags += ' ' * lb
2025-07-01 03:02:56.705 else:
2025-07-01 03:02:56.709 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:56.714 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:56.718 else:
2025-07-01 03:02:56.722 # the synch pair is identical
2025-07-01 03:02:56.727 yield '  ' + aelt
2025-07-01 03:02:56.731
2025-07-01 03:02:56.735 # pump out diffs from after the synch point
2025-07-01 03:02:56.740 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:56.744
2025-07-01 03:02:56.748 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:56.753 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:56.757
2025-07-01 03:02:56.761 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:56.766 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:56.772 alo = 273, ahi = 1101
2025-07-01 03:02:56.778 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:56.784 blo = 273, bhi = 1101
2025-07-01 03:02:56.788
2025-07-01 03:02:56.792 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:56.797 g = []
2025-07-01 03:02:56.801 if alo < ahi:
2025-07-01 03:02:56.805 if blo < bhi:
2025-07-01 03:02:56.810 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:56.814 else:
2025-07-01 03:02:56.819 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:56.823 elif blo < bhi:
2025-07-01 03:02:56.827 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:56.831
2025-07-01 03:02:56.836 >       yield from g
2025-07-01 03:02:56.840
2025-07-01 03:02:56.844 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:56.849 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:56.853
2025-07-01 03:02:56.857 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:56.862 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:56.866 alo = 273, ahi = 1101
2025-07-01 03:02:56.871 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:56.875 blo = 273, bhi = 1101
2025-07-01 03:02:56.879
2025-07-01 03:02:56.884 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:56.888 r"""
2025-07-01 03:02:56.892 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:56.896 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:56.901 synch point, and intraline difference marking is done on the
2025-07-01 03:02:56.905 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:56.909
2025-07-01 03:02:56.913 Example:
2025-07-01 03:02:56.918
2025-07-01 03:02:56.922 >>> d = Differ()
2025-07-01 03:02:56.926 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:56.931 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:56.935 >>> print(''.join(results), end="")
2025-07-01 03:02:56.939 - abcDefghiJkl
2025-07-01 03:02:56.950 + abcdefGhijkl
2025-07-01 03:02:56.962 """
2025-07-01 03:02:56.967
2025-07-01 03:02:56.973 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:56.979 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:56.985 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:56.990 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:56.995 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:57.001
2025-07-01 03:02:57.007 # search for the pair that matches best without being identical
2025-07-01 03:02:57.013 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:57.019 # on junk -- unless we have to)
2025-07-01 03:02:57.024 for j in range(blo, bhi):
2025-07-01 03:02:57.030 bj = b[j]
2025-07-01 03:02:57.035 cruncher.set_seq2(bj)
2025-07-01 03:02:57.039 for i in range(alo, ahi):
2025-07-01 03:02:57.043 ai = a[i]
2025-07-01 03:02:57.048 if ai == bj:
2025-07-01 03:02:57.052 if eqi is None:
2025-07-01 03:02:57.057 eqi, eqj = i, j
2025-07-01 03:02:57.061 continue
2025-07-01 03:02:57.065 cruncher.set_seq1(ai)
2025-07-01 03:02:57.070 # computing similarity is expensive, so use the quick
2025-07-01 03:02:57.074 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:57.078 # compares by a factor of 3.
2025-07-01 03:02:57.083 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:57.088 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:57.092 # of the computation is cached by cruncher
2025-07-01 03:02:57.096 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:57.101 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:57.105 cruncher.ratio() > best_ratio:
2025-07-01 03:02:57.110 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:57.114 if best_ratio < cutoff:
2025-07-01 03:02:57.118 # no non-identical "pretty close" pair
2025-07-01 03:02:57.123 if eqi is None:
2025-07-01 03:02:57.127 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:57.132 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:57.137 return
2025-07-01 03:02:57.141 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:57.146 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:57.150 else:
2025-07-01 03:02:57.155 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:57.160 eqi = None
2025-07-01 03:02:57.166
2025-07-01 03:02:57.171 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:57.175 # identical
2025-07-01 03:02:57.180
2025-07-01 03:02:57.184 # pump out diffs from before the synch point
2025-07-01 03:02:57.189 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:57.193
2025-07-01 03:02:57.197 # do intraline marking on the synch pair
2025-07-01 03:02:57.202 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:57.207 if eqi is None:
2025-07-01 03:02:57.212 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:57.217 atags = btags = ""
2025-07-01 03:02:57.221 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:57.226 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:57.230 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:57.235 if tag == 'replace':
2025-07-01 03:02:57.239 atags += '^' * la
2025-07-01 03:02:57.243 btags += '^' * lb
2025-07-01 03:02:57.248 elif tag == 'delete':
2025-07-01 03:02:57.253 atags += '-' * la
2025-07-01 03:02:57.258 elif tag == 'insert':
2025-07-01 03:02:57.262 btags += '+' * lb
2025-07-01 03:02:57.267 elif tag == 'equal':
2025-07-01 03:02:57.271 atags += ' ' * la
2025-07-01 03:02:57.276 btags += ' ' * lb
2025-07-01 03:02:57.280 else:
2025-07-01 03:02:57.285 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:57.289 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:57.293 else:
2025-07-01 03:02:57.298 # the synch pair is identical
2025-07-01 03:02:57.302 yield '  ' + aelt
2025-07-01 03:02:57.306
2025-07-01 03:02:57.311 # pump out diffs from after the synch point
2025-07-01 03:02:57.315 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:57.319
2025-07-01 03:02:57.324 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:57.328 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:57.332
2025-07-01 03:02:57.337 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:57.341 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:57.346 alo = 274, ahi = 1101
2025-07-01 03:02:57.351 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:57.355 blo = 274, bhi = 1101
2025-07-01 03:02:57.360
2025-07-01 03:02:57.365 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:57.369 g = []
2025-07-01 03:02:57.373 if alo < ahi:
2025-07-01 03:02:57.378 if blo < bhi:
2025-07-01 03:02:57.382 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:57.387 else:
2025-07-01 03:02:57.391 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:57.396 elif blo < bhi:
2025-07-01 03:02:57.400 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:57.405
2025-07-01 03:02:57.409 >       yield from g
2025-07-01 03:02:57.413
2025-07-01 03:02:57.418 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:57.423 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:57.427
2025-07-01 03:02:57.432 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:57.437 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:57.442 alo = 274, ahi = 1101
2025-07-01 03:02:57.448 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:57.453 blo = 274, bhi = 1101
2025-07-01 03:02:57.458
2025-07-01 03:02:57.469 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:57.474 r"""
2025-07-01 03:02:57.479 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:57.485 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:57.490 synch point, and intraline difference marking is done on the
2025-07-01 03:02:57.495 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:57.500
2025-07-01 03:02:57.504 Example:
2025-07-01 03:02:57.509
2025-07-01 03:02:57.515 >>> d = Differ()
2025-07-01 03:02:57.520 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:57.526 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:57.531 >>> print(''.join(results), end="")
2025-07-01 03:02:57.537 - abcDefghiJkl
2025-07-01 03:02:57.548 + abcdefGhijkl
2025-07-01 03:02:57.559 """
2025-07-01 03:02:57.563
2025-07-01 03:02:57.574 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:57.582 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:57.589 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:57.594 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:57.599 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:57.603
2025-07-01 03:02:57.608 # search for the pair that matches best without being identical
2025-07-01 03:02:57.613 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:57.618 # on junk -- unless we have to)
2025-07-01 03:02:57.622 for j in range(blo, bhi):
2025-07-01 03:02:57.628 bj = b[j]
2025-07-01 03:02:57.634 cruncher.set_seq2(bj)
2025-07-01 03:02:57.638 for i in range(alo, ahi):
2025-07-01 03:02:57.643 ai = a[i]
2025-07-01 03:02:57.647 if ai == bj:
2025-07-01 03:02:57.652 if eqi is None:
2025-07-01 03:02:57.656 eqi, eqj = i, j
2025-07-01 03:02:57.661 continue
2025-07-01 03:02:57.667 cruncher.set_seq1(ai)
2025-07-01 03:02:57.673 # computing similarity is expensive, so use the quick
2025-07-01 03:02:57.678 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:57.683 # compares by a factor of 3.
2025-07-01 03:02:57.688 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:57.692 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:57.697 # of the computation is cached by cruncher
2025-07-01 03:02:57.701 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:57.706 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:57.711 cruncher.ratio() > best_ratio:
2025-07-01 03:02:57.715 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:57.720 if best_ratio < cutoff:
2025-07-01 03:02:57.725 # no non-identical "pretty close" pair
2025-07-01 03:02:57.729 if eqi is None:
2025-07-01 03:02:57.734 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:57.739 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:57.744 return
2025-07-01 03:02:57.750 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:57.755 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:57.760 else:
2025-07-01 03:02:57.766 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:57.772 eqi = None
2025-07-01 03:02:57.777
2025-07-01 03:02:57.782 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:57.786 # identical
2025-07-01 03:02:57.791
2025-07-01 03:02:57.796 # pump out diffs from before the synch point
2025-07-01 03:02:57.802 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:57.806
2025-07-01 03:02:57.811 # do intraline marking on the synch pair
2025-07-01 03:02:57.817 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:57.822 if eqi is None:
2025-07-01 03:02:57.828 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:57.834 atags = btags = ""
2025-07-01 03:02:57.839 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:57.844 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:57.849 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:57.855 if tag == 'replace':
2025-07-01 03:02:57.861 atags += '^' * la
2025-07-01 03:02:57.867 btags += '^' * lb
2025-07-01 03:02:57.872 elif tag == 'delete':
2025-07-01 03:02:57.877 atags += '-' * la
2025-07-01 03:02:57.883 elif tag == 'insert':
2025-07-01 03:02:57.888 btags += '+' * lb
2025-07-01 03:02:57.893 elif tag == 'equal':
2025-07-01 03:02:57.897 atags += ' ' * la
2025-07-01 03:02:57.902 btags += ' ' * lb
2025-07-01 03:02:57.908 else:
2025-07-01 03:02:57.914 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:57.923 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:57.933 else:
2025-07-01 03:02:57.941 # the synch pair is identical
2025-07-01 03:02:57.946 yield '  ' + aelt
2025-07-01 03:02:57.951
2025-07-01 03:02:57.956 # pump out diffs from after the synch point
2025-07-01 03:02:57.961 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:57.965
2025-07-01 03:02:57.970 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:57.974 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:57.979
2025-07-01 03:02:57.983 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:57.989 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:57.994 alo = 275, ahi = 1101
2025-07-01 03:02:57.999 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:58.005 blo = 275, bhi = 1101
2025-07-01 03:02:58.010
2025-07-01 03:02:58.014 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:58.019 g = []
2025-07-01 03:02:58.024 if alo < ahi:
2025-07-01 03:02:58.028 if blo < bhi:
2025-07-01 03:02:58.033 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:58.037 else:
2025-07-01 03:02:58.042 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:58.046 elif blo < bhi:
2025-07-01 03:02:58.051 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:58.055
2025-07-01 03:02:58.060 >       yield from g
2025-07-01 03:02:58.065
2025-07-01 03:02:58.069 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:58.074 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:58.078
2025-07-01 03:02:58.083 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:58.088 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:58.092 alo = 275, ahi = 1101
2025-07-01 03:02:58.096 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:58.101 blo = 275, bhi = 1101
2025-07-01 03:02:58.105
2025-07-01 03:02:58.110 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:58.114 r"""
2025-07-01 03:02:58.118 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:58.123 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:58.128 synch point, and intraline difference marking is done on the
2025-07-01 03:02:58.132 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:58.137
2025-07-01 03:02:58.141 Example:
2025-07-01 03:02:58.145
2025-07-01 03:02:58.150 >>> d = Differ()
2025-07-01 03:02:58.154 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:58.159 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:58.163 >>> print(''.join(results), end="")
2025-07-01 03:02:58.168 - abcDefghiJkl
2025-07-01 03:02:58.177 + abcdefGhijkl
2025-07-01 03:02:58.186 """
2025-07-01 03:02:58.190
2025-07-01 03:02:58.195 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:58.199 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:58.204 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:58.209 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:58.213 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:58.217
2025-07-01 03:02:58.222 # search for the pair that matches best without being identical
2025-07-01 03:02:58.226 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:58.231 # on junk -- unless we have to)
2025-07-01 03:02:58.235 for j in range(blo, bhi):
2025-07-01 03:02:58.239 bj = b[j]
2025-07-01 03:02:58.244 cruncher.set_seq2(bj)
2025-07-01 03:02:58.248 for i in range(alo, ahi):
2025-07-01 03:02:58.253 ai = a[i]
2025-07-01 03:02:58.257 if ai == bj:
2025-07-01 03:02:58.263 if eqi is None:
2025-07-01 03:02:58.267 eqi, eqj = i, j
2025-07-01 03:02:58.272 continue
2025-07-01 03:02:58.277 cruncher.set_seq1(ai)
2025-07-01 03:02:58.282 # computing similarity is expensive, so use the quick
2025-07-01 03:02:58.288 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:58.294 # compares by a factor of 3.
2025-07-01 03:02:58.299 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:58.304 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:58.308 # of the computation is cached by cruncher
2025-07-01 03:02:58.313 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:58.317 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:58.323 cruncher.ratio() > best_ratio:
2025-07-01 03:02:58.327 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:58.331 if best_ratio < cutoff:
2025-07-01 03:02:58.336 # no non-identical "pretty close" pair
2025-07-01 03:02:58.340 if eqi is None:
2025-07-01 03:02:58.344 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:58.349 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:58.353 return
2025-07-01 03:02:58.358 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:58.363 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:58.368 else:
2025-07-01 03:02:58.373 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:58.377 eqi = None
2025-07-01 03:02:58.381
2025-07-01 03:02:58.386 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:58.390 # identical
2025-07-01 03:02:58.395
2025-07-01 03:02:58.399 # pump out diffs from before the synch point
2025-07-01 03:02:58.404 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:58.408
2025-07-01 03:02:58.413 # do intraline marking on the synch pair
2025-07-01 03:02:58.417 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:58.421 if eqi is None:
2025-07-01 03:02:58.426 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:58.431 atags = btags = ""
2025-07-01 03:02:58.435 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:58.441 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:58.445 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:58.450 if tag == 'replace':
2025-07-01 03:02:58.455 atags += '^' * la
2025-07-01 03:02:58.459 btags += '^' * lb
2025-07-01 03:02:58.463 elif tag == 'delete':
2025-07-01 03:02:58.468 atags += '-' * la
2025-07-01 03:02:58.472 elif tag == 'insert':
2025-07-01 03:02:58.477 btags += '+' * lb
2025-07-01 03:02:58.481 elif tag == 'equal':
2025-07-01 03:02:58.486 atags += ' ' * la
2025-07-01 03:02:58.490 btags += ' ' * lb
2025-07-01 03:02:58.494 else:
2025-07-01 03:02:58.499 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:58.503 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:58.508 else:
2025-07-01 03:02:58.512 # the synch pair is identical
2025-07-01 03:02:58.516 yield '  ' + aelt
2025-07-01 03:02:58.521
2025-07-01 03:02:58.525 # pump out diffs from after the synch point
2025-07-01 03:02:58.530 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:58.536
2025-07-01 03:02:58.540 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:58.545 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:58.549
2025-07-01 03:02:58.554 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:58.558 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:58.563 alo = 276, ahi = 1101
2025-07-01 03:02:58.568 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:58.572 blo = 276, bhi = 1101
2025-07-01 03:02:58.576
2025-07-01 03:02:58.581 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:58.585 g = []
2025-07-01 03:02:58.590 if alo < ahi:
2025-07-01 03:02:58.595 if blo < bhi:
2025-07-01 03:02:58.603 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:58.607 else:
2025-07-01 03:02:58.612 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:58.616 elif blo < bhi:
2025-07-01 03:02:58.621 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:58.626
2025-07-01 03:02:58.631 >       yield from g
2025-07-01 03:02:58.636
2025-07-01 03:02:58.640 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:58.645 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:58.649
2025-07-01 03:02:58.653 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:58.658 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:58.663 alo = 276, ahi = 1101
2025-07-01 03:02:58.668 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:58.672 blo = 276, bhi = 1101
2025-07-01 03:02:58.676
2025-07-01 03:02:58.681 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:58.685 r"""
2025-07-01 03:02:58.689 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:58.694 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:58.699 synch point, and intraline difference marking is done on the
2025-07-01 03:02:58.703 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:58.707
2025-07-01 03:02:58.712 Example:
2025-07-01 03:02:58.717
2025-07-01 03:02:58.722 >>> d = Differ()
2025-07-01 03:02:58.726 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:58.731 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:58.736 >>> print(''.join(results), end="")
2025-07-01 03:02:58.741 - abcDefghiJkl
2025-07-01 03:02:58.750 + abcdefGhijkl
2025-07-01 03:02:58.761 """
2025-07-01 03:02:58.767
2025-07-01 03:02:58.773 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:58.780 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:58.789 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:58.796 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:58.801 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:58.806
2025-07-01 03:02:58.811 # search for the pair that matches best without being identical
2025-07-01 03:02:58.822 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:58.833 # on junk -- unless we have to)
2025-07-01 03:02:58.838 for j in range(blo, bhi):
2025-07-01 03:02:58.843 bj = b[j]
2025-07-01 03:02:58.847 cruncher.set_seq2(bj)
2025-07-01 03:02:58.852 for i in range(alo, ahi):
2025-07-01 03:02:58.856 ai = a[i]
2025-07-01 03:02:58.861 if ai == bj:
2025-07-01 03:02:58.865 if eqi is None:
2025-07-01 03:02:58.870 eqi, eqj = i, j
2025-07-01 03:02:58.875 continue
2025-07-01 03:02:58.879 cruncher.set_seq1(ai)
2025-07-01 03:02:58.884 # computing similarity is expensive, so use the quick
2025-07-01 03:02:58.888 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:58.893 # compares by a factor of 3.
2025-07-01 03:02:58.897 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:58.902 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:58.908 # of the computation is cached by cruncher
2025-07-01 03:02:58.913 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:58.917 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:58.922 cruncher.ratio() > best_ratio:
2025-07-01 03:02:58.927 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:58.931 if best_ratio < cutoff:
2025-07-01 03:02:58.936 # no non-identical "pretty close" pair
2025-07-01 03:02:58.940 if eqi is None:
2025-07-01 03:02:58.945 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:58.949 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:58.954 return
2025-07-01 03:02:58.959 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:58.963 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:58.967 else:
2025-07-01 03:02:58.972 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:58.976 eqi = None
2025-07-01 03:02:58.981
2025-07-01 03:02:58.985 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:58.990 # identical
2025-07-01 03:02:58.994
2025-07-01 03:02:58.999 # pump out diffs from before the synch point
2025-07-01 03:02:59.003 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:59.008
2025-07-01 03:02:59.012 # do intraline marking on the synch pair
2025-07-01 03:02:59.017 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:59.021 if eqi is None:
2025-07-01 03:02:59.026 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:59.030 atags = btags = ""
2025-07-01 03:02:59.035 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:59.039 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:59.044 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:59.049 if tag == 'replace':
2025-07-01 03:02:59.055 atags += '^' * la
2025-07-01 03:02:59.060 btags += '^' * lb
2025-07-01 03:02:59.064 elif tag == 'delete':
2025-07-01 03:02:59.069 atags += '-' * la
2025-07-01 03:02:59.073 elif tag == 'insert':
2025-07-01 03:02:59.077 btags += '+' * lb
2025-07-01 03:02:59.081 elif tag == 'equal':
2025-07-01 03:02:59.086 atags += ' ' * la
2025-07-01 03:02:59.090 btags += ' ' * lb
2025-07-01 03:02:59.096 else:
2025-07-01 03:02:59.102 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:59.109 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:59.114 else:
2025-07-01 03:02:59.119 # the synch pair is identical
2025-07-01 03:02:59.124 yield '  ' + aelt
2025-07-01 03:02:59.129
2025-07-01 03:02:59.134 # pump out diffs from after the synch point
2025-07-01 03:02:59.139 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:59.144
2025-07-01 03:02:59.149 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:59.154 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:59.159
2025-07-01 03:02:59.165 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:59.170 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:59.174 alo = 277, ahi = 1101
2025-07-01 03:02:59.179 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:59.186 blo = 277, bhi = 1101
2025-07-01 03:02:59.191
2025-07-01 03:02:59.197 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:59.201 g = []
2025-07-01 03:02:59.206 if alo < ahi:
2025-07-01 03:02:59.211 if blo < bhi:
2025-07-01 03:02:59.216 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:59.221 else:
2025-07-01 03:02:59.225 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:59.230 elif blo < bhi:
2025-07-01 03:02:59.235 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:59.240
2025-07-01 03:02:59.245 >       yield from g
2025-07-01 03:02:59.250
2025-07-01 03:02:59.254 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:59.259 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:59.264
2025-07-01 03:02:59.268 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:59.273 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:59.277 alo = 277, ahi = 1101
2025-07-01 03:02:59.282 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:59.287 blo = 277, bhi = 1101
2025-07-01 03:02:59.292
2025-07-01 03:02:59.297 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:59.306 r"""
2025-07-01 03:02:59.311 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:59.316 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:59.321 synch point, and intraline difference marking is done on the
2025-07-01 03:02:59.326 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:59.331
2025-07-01 03:02:59.336 Example:
2025-07-01 03:02:59.341
2025-07-01 03:02:59.346 >>> d = Differ()
2025-07-01 03:02:59.351 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:59.356 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:59.361 >>> print(''.join(results), end="")
2025-07-01 03:02:59.365 - abcDefghiJkl
2025-07-01 03:02:59.374 + abcdefGhijkl
2025-07-01 03:02:59.383 """
2025-07-01 03:02:59.388
2025-07-01 03:02:59.392 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:02:59.397 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:02:59.401 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:02:59.406 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:02:59.410 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:02:59.414
2025-07-01 03:02:59.419 # search for the pair that matches best without being identical
2025-07-01 03:02:59.423 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:02:59.428 # on junk -- unless we have to)
2025-07-01 03:02:59.432 for j in range(blo, bhi):
2025-07-01 03:02:59.437 bj = b[j]
2025-07-01 03:02:59.441 cruncher.set_seq2(bj)
2025-07-01 03:02:59.445 for i in range(alo, ahi):
2025-07-01 03:02:59.450 ai = a[i]
2025-07-01 03:02:59.454 if ai == bj:
2025-07-01 03:02:59.458 if eqi is None:
2025-07-01 03:02:59.463 eqi, eqj = i, j
2025-07-01 03:02:59.467 continue
2025-07-01 03:02:59.472 cruncher.set_seq1(ai)
2025-07-01 03:02:59.476 # computing similarity is expensive, so use the quick
2025-07-01 03:02:59.481 # upper bounds first -- have seen this speed up messy
2025-07-01 03:02:59.485 # compares by a factor of 3.
2025-07-01 03:02:59.490 # note that ratio() is only expensive to compute the first
2025-07-01 03:02:59.495 # time it's called on a sequence pair; the expensive part
2025-07-01 03:02:59.499 # of the computation is cached by cruncher
2025-07-01 03:02:59.504 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:02:59.508 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:02:59.513 cruncher.ratio() > best_ratio:
2025-07-01 03:02:59.517 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:02:59.522 if best_ratio < cutoff:
2025-07-01 03:02:59.526 # no non-identical "pretty close" pair
2025-07-01 03:02:59.531 if eqi is None:
2025-07-01 03:02:59.535 # no identical pair either -- treat it as a straight replace
2025-07-01 03:02:59.540 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:59.544 return
2025-07-01 03:02:59.549 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:02:59.553 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:02:59.558 else:
2025-07-01 03:02:59.562 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:02:59.566 eqi = None
2025-07-01 03:02:59.575
2025-07-01 03:02:59.580 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:02:59.585 # identical
2025-07-01 03:02:59.589
2025-07-01 03:02:59.594 # pump out diffs from before the synch point
2025-07-01 03:02:59.599 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:02:59.604
2025-07-01 03:02:59.608 # do intraline marking on the synch pair
2025-07-01 03:02:59.613 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:02:59.617 if eqi is None:
2025-07-01 03:02:59.622 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:02:59.626 atags = btags = ""
2025-07-01 03:02:59.631 cruncher.set_seqs(aelt, belt)
2025-07-01 03:02:59.635 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:02:59.640 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:02:59.645 if tag == 'replace':
2025-07-01 03:02:59.649 atags += '^' * la
2025-07-01 03:02:59.653 btags += '^' * lb
2025-07-01 03:02:59.658 elif tag == 'delete':
2025-07-01 03:02:59.662 atags += '-' * la
2025-07-01 03:02:59.666 elif tag == 'insert':
2025-07-01 03:02:59.672 btags += '+' * lb
2025-07-01 03:02:59.676 elif tag == 'equal':
2025-07-01 03:02:59.681 atags += ' ' * la
2025-07-01 03:02:59.685 btags += ' ' * lb
2025-07-01 03:02:59.691 else:
2025-07-01 03:02:59.696 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:02:59.701 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:02:59.705 else:
2025-07-01 03:02:59.710 # the synch pair is identical
2025-07-01 03:02:59.714 yield '  ' + aelt
2025-07-01 03:02:59.719
2025-07-01 03:02:59.724 # pump out diffs from after the synch point
2025-07-01 03:02:59.728 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:02:59.733
2025-07-01 03:02:59.737 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:02:59.742 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:59.747
2025-07-01 03:02:59.754 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:59.761 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:59.766 alo = 278, ahi = 1101
2025-07-01 03:02:59.772 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:59.777 blo = 278, bhi = 1101
2025-07-01 03:02:59.782
2025-07-01 03:02:59.788 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:59.793 g = []
2025-07-01 03:02:59.798 if alo < ahi:
2025-07-01 03:02:59.803 if blo < bhi:
2025-07-01 03:02:59.808 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:02:59.814 else:
2025-07-01 03:02:59.820 g = self._dump('-', a, alo, ahi)
2025-07-01 03:02:59.825 elif blo < bhi:
2025-07-01 03:02:59.830 g = self._dump('+', b, blo, bhi)
2025-07-01 03:02:59.835
2025-07-01 03:02:59.841 >       yield from g
2025-07-01 03:02:59.846
2025-07-01 03:02:59.851 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:02:59.857 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:02:59.861
2025-07-01 03:02:59.866 self = <difflib.Differ object at [hex]>
2025-07-01 03:02:59.871 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:02:59.876 alo = 278, ahi = 1101
2025-07-01 03:02:59.882 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:02:59.887 blo = 278, bhi = 1101
2025-07-01 03:02:59.891
2025-07-01 03:02:59.897 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:02:59.902 r"""
2025-07-01 03:02:59.908 When replacing one block of lines with another, search the blocks
2025-07-01 03:02:59.916 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:02:59.921 synch point, and intraline difference marking is done on the
2025-07-01 03:02:59.926 similar pair. Lots of work, but often worth it.
2025-07-01 03:02:59.931
2025-07-01 03:02:59.936 Example:
2025-07-01 03:02:59.942
2025-07-01 03:02:59.947 >>> d = Differ()
2025-07-01 03:02:59.952 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:02:59.957 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:02:59.962 >>> print(''.join(results), end="")
2025-07-01 03:02:59.968 - abcDefghiJkl
2025-07-01 03:02:59.978 + abcdefGhijkl
2025-07-01 03:02:59.988 """
2025-07-01 03:02:59.994
2025-07-01 03:02:59.999 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:00.004 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:00.010 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:00.015 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:00.022 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:00.027
2025-07-01 03:03:00.033 # search for the pair that matches best without being identical
2025-07-01 03:03:00.039 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:00.045 # on junk -- unless we have to)
2025-07-01 03:03:00.051 for j in range(blo, bhi):
2025-07-01 03:03:00.057 bj = b[j]
2025-07-01 03:03:00.062 cruncher.set_seq2(bj)
2025-07-01 03:03:00.067 for i in range(alo, ahi):
2025-07-01 03:03:00.072 ai = a[i]
2025-07-01 03:03:00.078 if ai == bj:
2025-07-01 03:03:00.084 if eqi is None:
2025-07-01 03:03:00.089 eqi, eqj = i, j
2025-07-01 03:03:00.094 continue
2025-07-01 03:03:00.098 cruncher.set_seq1(ai)
2025-07-01 03:03:00.103 # computing similarity is expensive, so use the quick
2025-07-01 03:03:00.108 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:00.112 # compares by a factor of 3.
2025-07-01 03:03:00.117 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:00.123 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:00.128 # of the computation is cached by cruncher
2025-07-01 03:03:00.134 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:00.139 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:00.144 cruncher.ratio() > best_ratio:
2025-07-01 03:03:00.150 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:00.156 if best_ratio < cutoff:
2025-07-01 03:03:00.163 # no non-identical "pretty close" pair
2025-07-01 03:03:00.169 if eqi is None:
2025-07-01 03:03:00.178 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:00.187 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:00.193 return
2025-07-01 03:03:00.198 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:00.204 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:00.210 else:
2025-07-01 03:03:00.216 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:00.221 eqi = None
2025-07-01 03:03:00.226
2025-07-01 03:03:00.232 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:00.238 # identical
2025-07-01 03:03:00.244
2025-07-01 03:03:00.250 # pump out diffs from before the synch point
2025-07-01 03:03:00.263 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:00.272
2025-07-01 03:03:00.278 # do intraline marking on the synch pair
2025-07-01 03:03:00.284 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:00.289 if eqi is None:
2025-07-01 03:03:00.295 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:00.300 atags = btags = ""
2025-07-01 03:03:00.305 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:00.309 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:00.314 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:00.319 if tag == 'replace':
2025-07-01 03:03:00.323 atags += '^' * la
2025-07-01 03:03:00.328 btags += '^' * lb
2025-07-01 03:03:00.332 elif tag == 'delete':
2025-07-01 03:03:00.337 atags += '-' * la
2025-07-01 03:03:00.342 elif tag == 'insert':
2025-07-01 03:03:00.347 btags += '+' * lb
2025-07-01 03:03:00.353 elif tag == 'equal':
2025-07-01 03:03:00.358 atags += ' ' * la
2025-07-01 03:03:00.363 btags += ' ' * lb
2025-07-01 03:03:00.368 else:
2025-07-01 03:03:00.374 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:00.379 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:00.385 else:
2025-07-01 03:03:00.391 # the synch pair is identical
2025-07-01 03:03:00.397 yield '  ' + aelt
2025-07-01 03:03:00.402
2025-07-01 03:03:00.406 # pump out diffs from after the synch point
2025-07-01 03:03:00.411 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:00.416
2025-07-01 03:03:00.421 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:00.426 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:00.431
2025-07-01 03:03:00.438 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:00.443 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:00.447 alo = 279, ahi = 1101
2025-07-01 03:03:00.453 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:00.458 blo = 279, bhi = 1101
2025-07-01 03:03:00.463
2025-07-01 03:03:00.468 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:00.472 g = []
2025-07-01 03:03:00.477 if alo < ahi:
2025-07-01 03:03:00.481 if blo < bhi:
2025-07-01 03:03:00.486 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:00.490 else:
2025-07-01 03:03:00.495 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:00.501 elif blo < bhi:
2025-07-01 03:03:00.505 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:00.510
2025-07-01 03:03:00.514 >       yield from g
2025-07-01 03:03:00.519
2025-07-01 03:03:00.523 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:00.528 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:00.532
2025-07-01 03:03:00.537 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:00.541 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:00.546 alo = 279, ahi = 1101
2025-07-01 03:03:00.551 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:00.555 blo = 279, bhi = 1101
2025-07-01 03:03:00.560
2025-07-01 03:03:00.565 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:00.569 r"""
2025-07-01 03:03:00.575 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:00.580 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:00.586 synch point, and intraline difference marking is done on the
2025-07-01 03:03:00.591 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:00.596
2025-07-01 03:03:00.601 Example:
2025-07-01 03:03:00.607
2025-07-01 03:03:00.612 >>> d = Differ()
2025-07-01 03:03:00.617 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:00.623 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:00.628 >>> print(''.join(results), end="")
2025-07-01 03:03:00.638 - abcDefghiJkl
2025-07-01 03:03:00.648 + abcdefGhijkl
2025-07-01 03:03:00.659 """
2025-07-01 03:03:00.665
2025-07-01 03:03:00.670 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:00.678 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:00.684 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:00.689 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:00.695 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:00.701
2025-07-01 03:03:00.707 # search for the pair that matches best without being identical
2025-07-01 03:03:00.713 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:00.719 # on junk -- unless we have to)
2025-07-01 03:03:00.725 for j in range(blo, bhi):
2025-07-01 03:03:00.731 bj = b[j]
2025-07-01 03:03:00.736 cruncher.set_seq2(bj)
2025-07-01 03:03:00.741 for i in range(alo, ahi):
2025-07-01 03:03:00.746 ai = a[i]
2025-07-01 03:03:00.752 if ai == bj:
2025-07-01 03:03:00.758 if eqi is None:
2025-07-01 03:03:00.763 eqi, eqj = i, j
2025-07-01 03:03:00.769 continue
2025-07-01 03:03:00.774 cruncher.set_seq1(ai)
2025-07-01 03:03:00.779 # computing similarity is expensive, so use the quick
2025-07-01 03:03:00.784 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:00.788 # compares by a factor of 3.
2025-07-01 03:03:00.793 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:00.798 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:00.802 # of the computation is cached by cruncher
2025-07-01 03:03:00.807 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:00.812 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:00.817 cruncher.ratio() > best_ratio:
2025-07-01 03:03:00.822 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:00.826 if best_ratio < cutoff:
2025-07-01 03:03:00.831 # no non-identical "pretty close" pair
2025-07-01 03:03:00.835 if eqi is None:
2025-07-01 03:03:00.840 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:00.845 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:00.852 return
2025-07-01 03:03:00.858 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:00.864 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:00.870 else:
2025-07-01 03:03:00.877 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:00.883 eqi = None
2025-07-01 03:03:00.889
2025-07-01 03:03:00.895 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:00.902 # identical
2025-07-01 03:03:00.907
2025-07-01 03:03:00.914 # pump out diffs from before the synch point
2025-07-01 03:03:00.920 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:00.926
2025-07-01 03:03:00.932 # do intraline marking on the synch pair
2025-07-01 03:03:00.939 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:00.945 if eqi is None:
2025-07-01 03:03:00.951 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:00.957 atags = btags = ""
2025-07-01 03:03:00.967 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:00.974 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:00.980 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:00.987 if tag == 'replace':
2025-07-01 03:03:00.993 atags += '^' * la
2025-07-01 03:03:01.000 btags += '^' * lb
2025-07-01 03:03:01.005 elif tag == 'delete':
2025-07-01 03:03:01.010 atags += '-' * la
2025-07-01 03:03:01.014 elif tag == 'insert':
2025-07-01 03:03:01.019 btags += '+' * lb
2025-07-01 03:03:01.023 elif tag == 'equal':
2025-07-01 03:03:01.028 atags += ' ' * la
2025-07-01 03:03:01.032 btags += ' ' * lb
2025-07-01 03:03:01.037 else:
2025-07-01 03:03:01.042 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:01.046 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:01.051 else:
2025-07-01 03:03:01.055 # the synch pair is identical
2025-07-01 03:03:01.060 yield '  ' + aelt
2025-07-01 03:03:01.064
2025-07-01 03:03:01.069 # pump out diffs from after the synch point
2025-07-01 03:03:01.073 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:01.078
2025-07-01 03:03:01.082 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:01.087 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:01.091
2025-07-01 03:03:01.096 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:01.101 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:01.105 alo = 280, ahi = 1101
2025-07-01 03:03:01.111 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:01.115 blo = 280, bhi = 1101
2025-07-01 03:03:01.120
2025-07-01 03:03:01.124 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:01.129 g = []
2025-07-01 03:03:01.133 if alo < ahi:
2025-07-01 03:03:01.138 if blo < bhi:
2025-07-01 03:03:01.142 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:01.146 else:
2025-07-01 03:03:01.151 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:01.155 elif blo < bhi:
2025-07-01 03:03:01.160 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:01.164
2025-07-01 03:03:01.169 >       yield from g
2025-07-01 03:03:01.173
2025-07-01 03:03:01.177 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:01.182 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:01.186
2025-07-01 03:03:01.191 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:01.195 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:01.199 alo = 280, ahi = 1101
2025-07-01 03:03:01.204 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:01.209 blo = 280, bhi = 1101
2025-07-01 03:03:01.213
2025-07-01 03:03:01.220 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:01.225 r"""
2025-07-01 03:03:01.229 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:01.234 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:01.238 synch point, and intraline difference marking is done on the
2025-07-01 03:03:01.242 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:01.247
2025-07-01 03:03:01.251 Example:
2025-07-01 03:03:01.255
2025-07-01 03:03:01.259 >>> d = Differ()
2025-07-01 03:03:01.264 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:01.268 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:01.273 >>> print(''.join(results), end="")
2025-07-01 03:03:01.277 - abcDefghiJkl
2025-07-01 03:03:01.285 + abcdefGhijkl
2025-07-01 03:03:01.294 """
2025-07-01 03:03:01.299
2025-07-01 03:03:01.303 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:01.308 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:01.312 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:01.317 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:01.322 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:01.326
2025-07-01 03:03:01.331 # search for the pair that matches best without being identical
2025-07-01 03:03:01.336 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:01.340 # on junk -- unless we have to)
2025-07-01 03:03:01.345 for j in range(blo, bhi):
2025-07-01 03:03:01.350 bj = b[j]
2025-07-01 03:03:01.355 cruncher.set_seq2(bj)
2025-07-01 03:03:01.359 for i in range(alo, ahi):
2025-07-01 03:03:01.364 ai = a[i]
2025-07-01 03:03:01.369 if ai == bj:
2025-07-01 03:03:01.374 if eqi is None:
2025-07-01 03:03:01.378 eqi, eqj = i, j
2025-07-01 03:03:01.383 continue
2025-07-01 03:03:01.387 cruncher.set_seq1(ai)
2025-07-01 03:03:01.392 # computing similarity is expensive, so use the quick
2025-07-01 03:03:01.396 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:01.401 # compares by a factor of 3.
2025-07-01 03:03:01.406 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:01.411 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:01.416 # of the computation is cached by cruncher
2025-07-01 03:03:01.421 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:01.426 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:01.430 cruncher.ratio() > best_ratio:
2025-07-01 03:03:01.435 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:01.440 if best_ratio < cutoff:
2025-07-01 03:03:01.444 # no non-identical "pretty close" pair
2025-07-01 03:03:01.448 if eqi is None:
2025-07-01 03:03:01.453 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:01.457 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:01.462 return
2025-07-01 03:03:01.466 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:01.471 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:01.475 else:
2025-07-01 03:03:01.479 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:01.484 eqi = None
2025-07-01 03:03:01.488
2025-07-01 03:03:01.493 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:01.499 # identical
2025-07-01 03:03:01.503
2025-07-01 03:03:01.508 # pump out diffs from before the synch point
2025-07-01 03:03:01.514 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:01.519
2025-07-01 03:03:01.525 # do intraline marking on the synch pair
2025-07-01 03:03:01.531 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:01.537 if eqi is None:
2025-07-01 03:03:01.543 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:01.549 atags = btags = ""
2025-07-01 03:03:01.555 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:01.564 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:01.570 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:01.576 if tag == 'replace':
2025-07-01 03:03:01.582 atags += '^' * la
2025-07-01 03:03:01.588 btags += '^' * lb
2025-07-01 03:03:01.594 elif tag == 'delete':
2025-07-01 03:03:01.600 atags += '-' * la
2025-07-01 03:03:01.606 elif tag == 'insert':
2025-07-01 03:03:01.612 btags += '+' * lb
2025-07-01 03:03:01.618 elif tag == 'equal':
2025-07-01 03:03:01.624 atags += ' ' * la
2025-07-01 03:03:01.630 btags += ' ' * lb
2025-07-01 03:03:01.636 else:
2025-07-01 03:03:01.643 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:01.649 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:01.655 else:
2025-07-01 03:03:01.662 # the synch pair is identical
2025-07-01 03:03:01.669 yield '  ' + aelt
2025-07-01 03:03:01.675
2025-07-01 03:03:01.682 # pump out diffs from after the synch point
2025-07-01 03:03:01.689 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:01.695
2025-07-01 03:03:01.701 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:01.708 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:01.714
2025-07-01 03:03:01.720 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:01.727 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:01.732 alo = 281, ahi = 1101
2025-07-01 03:03:01.739 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:01.743 blo = 281, bhi = 1101
2025-07-01 03:03:01.748
2025-07-01 03:03:01.752 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:01.757 g = []
2025-07-01 03:03:01.761 if alo < ahi:
2025-07-01 03:03:01.765 if blo < bhi:
2025-07-01 03:03:01.771 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:01.778 else:
2025-07-01 03:03:01.784 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:01.790 elif blo < bhi:
2025-07-01 03:03:01.797 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:01.803
2025-07-01 03:03:01.809 >       yield from g
2025-07-01 03:03:01.816
2025-07-01 03:03:01.822 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:01.828 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:01.834
2025-07-01 03:03:01.840 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:01.847 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:01.853 alo = 281, ahi = 1101
2025-07-01 03:03:01.859 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:01.866 blo = 281, bhi = 1101
2025-07-01 03:03:01.871
2025-07-01 03:03:01.877 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:01.883 r"""
2025-07-01 03:03:01.889 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:01.895 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:01.901 synch point, and intraline difference marking is done on the
2025-07-01 03:03:01.907 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:01.913
2025-07-01 03:03:01.918 Example:
2025-07-01 03:03:01.924
2025-07-01 03:03:01.930 >>> d = Differ()
2025-07-01 03:03:01.936 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:01.942 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:01.948 >>> print(''.join(results), end="")
2025-07-01 03:03:01.954 - abcDefghiJkl
2025-07-01 03:03:01.965 + abcdefGhijkl
2025-07-01 03:03:01.977 """
2025-07-01 03:03:01.982
2025-07-01 03:03:01.988 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:01.994 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:01.999 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:02.004 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:02.008 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:02.013
2025-07-01 03:03:02.017 # search for the pair that matches best without being identical
2025-07-01 03:03:02.022 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:02.026 # on junk -- unless we have to)
2025-07-01 03:03:02.031 for j in range(blo, bhi):
2025-07-01 03:03:02.035 bj = b[j]
2025-07-01 03:03:02.039 cruncher.set_seq2(bj)
2025-07-01 03:03:02.044 for i in range(alo, ahi):
2025-07-01 03:03:02.048 ai = a[i]
2025-07-01 03:03:02.053 if ai == bj:
2025-07-01 03:03:02.057 if eqi is None:
2025-07-01 03:03:02.061 eqi, eqj = i, j
2025-07-01 03:03:02.066 continue
2025-07-01 03:03:02.070 cruncher.set_seq1(ai)
2025-07-01 03:03:02.074 # computing similarity is expensive, so use the quick
2025-07-01 03:03:02.079 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:02.083 # compares by a factor of 3.
2025-07-01 03:03:02.088 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:02.092 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:02.096 # of the computation is cached by cruncher
2025-07-01 03:03:02.101 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:02.105 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:02.110 cruncher.ratio() > best_ratio:
2025-07-01 03:03:02.114 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:02.118 if best_ratio < cutoff:
2025-07-01 03:03:02.123 # no non-identical "pretty close" pair
2025-07-01 03:03:02.127 if eqi is None:
2025-07-01 03:03:02.132 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:02.136 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:02.141 return
2025-07-01 03:03:02.145 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:02.150 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:02.154 else:
2025-07-01 03:03:02.158 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:02.163 eqi = None
2025-07-01 03:03:02.167
2025-07-01 03:03:02.171 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:02.176 # identical
2025-07-01 03:03:02.180
2025-07-01 03:03:02.184 # pump out diffs from before the synch point
2025-07-01 03:03:02.189 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:02.193
2025-07-01 03:03:02.197 # do intraline marking on the synch pair
2025-07-01 03:03:02.202 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:02.206 if eqi is None:
2025-07-01 03:03:02.211 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:02.215 atags = btags = ""
2025-07-01 03:03:02.220 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:02.224 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:02.230 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:02.234 if tag == 'replace':
2025-07-01 03:03:02.239 atags += '^' * la
2025-07-01 03:03:02.243 btags += '^' * lb
2025-07-01 03:03:02.247 elif tag == 'delete':
2025-07-01 03:03:02.252 atags += '-' * la
2025-07-01 03:03:02.256 elif tag == 'insert':
2025-07-01 03:03:02.261 btags += '+' * lb
2025-07-01 03:03:02.265 elif tag == 'equal':
2025-07-01 03:03:02.270 atags += ' ' * la
2025-07-01 03:03:02.274 btags += ' ' * lb
2025-07-01 03:03:02.279 else:
2025-07-01 03:03:02.283 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:02.288 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:02.292 else:
2025-07-01 03:03:02.296 # the synch pair is identical
2025-07-01 03:03:02.301 yield '  ' + aelt
2025-07-01 03:03:02.305
2025-07-01 03:03:02.311 # pump out diffs from after the synch point
2025-07-01 03:03:02.317 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:02.322
2025-07-01 03:03:02.327 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:02.333 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:02.338
2025-07-01 03:03:02.343 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:02.349 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:02.355 alo = 282, ahi = 1101
2025-07-01 03:03:02.361 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:02.366 blo = 282, bhi = 1101
2025-07-01 03:03:02.372
2025-07-01 03:03:02.378 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:02.383 g = []
2025-07-01 03:03:02.388 if alo < ahi:
2025-07-01 03:03:02.394 if blo < bhi:
2025-07-01 03:03:02.400 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:02.404 else:
2025-07-01 03:03:02.410 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:02.416 elif blo < bhi:
2025-07-01 03:03:02.421 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:02.426
2025-07-01 03:03:02.432 >       yield from g
2025-07-01 03:03:02.438
2025-07-01 03:03:02.443 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:02.449 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:02.454
2025-07-01 03:03:02.459 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:02.465 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:02.474 alo = 282, ahi = 1101
2025-07-01 03:03:02.479 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:02.485 blo = 282, bhi = 1101
2025-07-01 03:03:02.490
2025-07-01 03:03:02.496 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:02.502 r"""
2025-07-01 03:03:02.508 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:02.513 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:02.518 synch point, and intraline difference marking is done on the
2025-07-01 03:03:02.523 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:02.529
2025-07-01 03:03:02.533 Example:
2025-07-01 03:03:02.538
2025-07-01 03:03:02.544 >>> d = Differ()
2025-07-01 03:03:02.549 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:02.555 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:02.561 >>> print(''.join(results), end="")
2025-07-01 03:03:02.566 - abcDefghiJkl
2025-07-01 03:03:02.575 + abcdefGhijkl
2025-07-01 03:03:02.584 """
2025-07-01 03:03:02.589
2025-07-01 03:03:02.594 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:02.599 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:02.603 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:02.609 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:02.614 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:02.618
2025-07-01 03:03:02.623 # search for the pair that matches best without being identical
2025-07-01 03:03:02.628 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:02.632 # on junk -- unless we have to)
2025-07-01 03:03:02.637 for j in range(blo, bhi):
2025-07-01 03:03:02.641 bj = b[j]
2025-07-01 03:03:02.646 cruncher.set_seq2(bj)
2025-07-01 03:03:02.651 for i in range(alo, ahi):
2025-07-01 03:03:02.660 ai = a[i]
2025-07-01 03:03:02.665 if ai == bj:
2025-07-01 03:03:02.671 if eqi is None:
2025-07-01 03:03:02.676 eqi, eqj = i, j
2025-07-01 03:03:02.681 continue
2025-07-01 03:03:02.687 cruncher.set_seq1(ai)
2025-07-01 03:03:02.693 # computing similarity is expensive, so use the quick
2025-07-01 03:03:02.699 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:02.705 # compares by a factor of 3.
2025-07-01 03:03:02.710 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:02.715 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:02.720 # of the computation is cached by cruncher
2025-07-01 03:03:02.724 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:02.729 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:02.734 cruncher.ratio() > best_ratio:
2025-07-01 03:03:02.738 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:02.743 if best_ratio < cutoff:
2025-07-01 03:03:02.748 # no non-identical "pretty close" pair
2025-07-01 03:03:02.753 if eqi is None:
2025-07-01 03:03:02.757 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:02.762 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:02.767 return
2025-07-01 03:03:02.772 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:02.776 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:02.781 else:
2025-07-01 03:03:02.787 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:02.795 eqi = None
2025-07-01 03:03:02.802
2025-07-01 03:03:02.809 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:02.820 # identical
2025-07-01 03:03:02.827
2025-07-01 03:03:02.833 # pump out diffs from before the synch point
2025-07-01 03:03:02.837 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:02.842
2025-07-01 03:03:02.846 # do intraline marking on the synch pair
2025-07-01 03:03:02.851 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:02.855 if eqi is None:
2025-07-01 03:03:02.860 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:02.865 atags = btags = ""
2025-07-01 03:03:02.870 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:02.875 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:02.879 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:02.884 if tag == 'replace':
2025-07-01 03:03:02.889 atags += '^' * la
2025-07-01 03:03:02.894 btags += '^' * lb
2025-07-01 03:03:02.898 elif tag == 'delete':
2025-07-01 03:03:02.903 atags += '-' * la
2025-07-01 03:03:02.908 elif tag == 'insert':
2025-07-01 03:03:02.913 btags += '+' * lb
2025-07-01 03:03:02.918 elif tag == 'equal':
2025-07-01 03:03:02.922 atags += ' ' * la
2025-07-01 03:03:02.927 btags += ' ' * lb
2025-07-01 03:03:02.932 else:
2025-07-01 03:03:02.936 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:02.941 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:02.946 else:
2025-07-01 03:03:02.950 # the synch pair is identical
2025-07-01 03:03:02.955 yield '  ' + aelt
2025-07-01 03:03:02.960
2025-07-01 03:03:02.964 # pump out diffs from after the synch point
2025-07-01 03:03:02.971 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:02.976
2025-07-01 03:03:02.981 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:02.985 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:02.990
2025-07-01 03:03:02.994 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:02.999 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:03.003 alo = 283, ahi = 1101
2025-07-01 03:03:03.009 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:03.013 blo = 283, bhi = 1101
2025-07-01 03:03:03.018
2025-07-01 03:03:03.022 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:03.027 g = []
2025-07-01 03:03:03.031 if alo < ahi:
2025-07-01 03:03:03.036 if blo < bhi:
2025-07-01 03:03:03.040 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:03.045 else:
2025-07-01 03:03:03.049 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:03.053 elif blo < bhi:
2025-07-01 03:03:03.058 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:03.063
2025-07-01 03:03:03.068 >       yield from g
2025-07-01 03:03:03.073
2025-07-01 03:03:03.077 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:03.082 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:03.086
2025-07-01 03:03:03.091 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:03.096 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:03.100 alo = 283, ahi = 1101
2025-07-01 03:03:03.106 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:03.110 blo = 283, bhi = 1101
2025-07-01 03:03:03.115
2025-07-01 03:03:03.120 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:03.124 r"""
2025-07-01 03:03:03.129 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:03.134 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:03.138 synch point, and intraline difference marking is done on the
2025-07-01 03:03:03.143 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:03.147
2025-07-01 03:03:03.158 Example:
2025-07-01 03:03:03.170
2025-07-01 03:03:03.179 >>> d = Differ()
2025-07-01 03:03:03.184 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:03.189 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:03.194 >>> print(''.join(results), end="")
2025-07-01 03:03:03.199 - abcDefghiJkl
2025-07-01 03:03:03.208 + abcdefGhijkl
2025-07-01 03:03:03.217 """
2025-07-01 03:03:03.221
2025-07-01 03:03:03.226 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:03.231 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:03.236 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:03.241 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:03.245 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:03.250
2025-07-01 03:03:03.255 # search for the pair that matches best without being identical
2025-07-01 03:03:03.260 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:03.264 # on junk -- unless we have to)
2025-07-01 03:03:03.269 for j in range(blo, bhi):
2025-07-01 03:03:03.274 bj = b[j]
2025-07-01 03:03:03.278 cruncher.set_seq2(bj)
2025-07-01 03:03:03.283 for i in range(alo, ahi):
2025-07-01 03:03:03.288 ai = a[i]
2025-07-01 03:03:03.293 if ai == bj:
2025-07-01 03:03:03.298 if eqi is None:
2025-07-01 03:03:03.302 eqi, eqj = i, j
2025-07-01 03:03:03.307 continue
2025-07-01 03:03:03.312 cruncher.set_seq1(ai)
2025-07-01 03:03:03.316 # computing similarity is expensive, so use the quick
2025-07-01 03:03:03.321 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:03.326 # compares by a factor of 3.
2025-07-01 03:03:03.331 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:03.336 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:03.341 # of the computation is cached by cruncher
2025-07-01 03:03:03.345 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:03.350 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:03.355 cruncher.ratio() > best_ratio:
2025-07-01 03:03:03.361 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:03.366 if best_ratio < cutoff:
2025-07-01 03:03:03.371 # no non-identical "pretty close" pair
2025-07-01 03:03:03.375 if eqi is None:
2025-07-01 03:03:03.380 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:03.385 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:03.390 return
2025-07-01 03:03:03.395 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:03.400 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:03.404 else:
2025-07-01 03:03:03.409 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:03.413 eqi = None
2025-07-01 03:03:03.418
2025-07-01 03:03:03.422 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:03.427 # identical
2025-07-01 03:03:03.432
2025-07-01 03:03:03.436 # pump out diffs from before the synch point
2025-07-01 03:03:03.441 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:03.445
2025-07-01 03:03:03.450 # do intraline marking on the synch pair
2025-07-01 03:03:03.454 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:03.459 if eqi is None:
2025-07-01 03:03:03.464 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:03.468 atags = btags = ""
2025-07-01 03:03:03.473 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:03.478 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:03.483 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:03.487 if tag == 'replace':
2025-07-01 03:03:03.492 atags += '^' * la
2025-07-01 03:03:03.497 btags += '^' * lb
2025-07-01 03:03:03.502 elif tag == 'delete':
2025-07-01 03:03:03.506 atags += '-' * la
2025-07-01 03:03:03.511 elif tag == 'insert':
2025-07-01 03:03:03.516 btags += '+' * lb
2025-07-01 03:03:03.520 elif tag == 'equal':
2025-07-01 03:03:03.525 atags += ' ' * la
2025-07-01 03:03:03.529 btags += ' ' * lb
2025-07-01 03:03:03.534 else:
2025-07-01 03:03:03.539 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:03.543 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:03.548 else:
2025-07-01 03:03:03.552 # the synch pair is identical
2025-07-01 03:03:03.557 yield '  ' + aelt
2025-07-01 03:03:03.561
2025-07-01 03:03:03.566 # pump out diffs from after the synch point
2025-07-01 03:03:03.570 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:03.575
2025-07-01 03:03:03.584 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:03.590 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:03.595
2025-07-01 03:03:03.600 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:03.606 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:03.612 alo = 284, ahi = 1101
2025-07-01 03:03:03.618 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:03.624 blo = 284, bhi = 1101
2025-07-01 03:03:03.629
2025-07-01 03:03:03.634 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:03.638 g = []
2025-07-01 03:03:03.643 if alo < ahi:
2025-07-01 03:03:03.648 if blo < bhi:
2025-07-01 03:03:03.653 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:03.657 else:
2025-07-01 03:03:03.662 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:03.666 elif blo < bhi:
2025-07-01 03:03:03.671 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:03.676
2025-07-01 03:03:03.680 >       yield from g
2025-07-01 03:03:03.685
2025-07-01 03:03:03.689 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:03.694 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:03.698
2025-07-01 03:03:03.703 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:03.707 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:03.712 alo = 284, ahi = 1101
2025-07-01 03:03:03.717 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:03.721 blo = 284, bhi = 1101
2025-07-01 03:03:03.726
2025-07-01 03:03:03.730 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:03.735 r"""
2025-07-01 03:03:03.739 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:03.744 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:03.750 synch point, and intraline difference marking is done on the
2025-07-01 03:03:03.754 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:03.758
2025-07-01 03:03:03.763 Example:
2025-07-01 03:03:03.767
2025-07-01 03:03:03.772 >>> d = Differ()
2025-07-01 03:03:03.777 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:03.781 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:03.786 >>> print(''.join(results), end="")
2025-07-01 03:03:03.791 - abcDefghiJkl
2025-07-01 03:03:03.800 + abcdefGhijkl
2025-07-01 03:03:03.812 """
2025-07-01 03:03:03.818
2025-07-01 03:03:03.824 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:03.830 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:03.836 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:03.842 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:03.847 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:03.852
2025-07-01 03:03:03.859 # search for the pair that matches best without being identical
2025-07-01 03:03:03.865 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:03.870 # on junk -- unless we have to)
2025-07-01 03:03:03.876 for j in range(blo, bhi):
2025-07-01 03:03:03.880 bj = b[j]
2025-07-01 03:03:03.885 cruncher.set_seq2(bj)
2025-07-01 03:03:03.889 for i in range(alo, ahi):
2025-07-01 03:03:03.894 ai = a[i]
2025-07-01 03:03:03.898 if ai == bj:
2025-07-01 03:03:03.904 if eqi is None:
2025-07-01 03:03:03.910 eqi, eqj = i, j
2025-07-01 03:03:03.916 continue
2025-07-01 03:03:03.920 cruncher.set_seq1(ai)
2025-07-01 03:03:03.925 # computing similarity is expensive, so use the quick
2025-07-01 03:03:03.931 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:03.937 # compares by a factor of 3.
2025-07-01 03:03:03.942 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:03.948 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:03.953 # of the computation is cached by cruncher
2025-07-01 03:03:03.958 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:03.963 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:03.968 cruncher.ratio() > best_ratio:
2025-07-01 03:03:03.973 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:03.979 if best_ratio < cutoff:
2025-07-01 03:03:03.984 # no non-identical "pretty close" pair
2025-07-01 03:03:03.989 if eqi is None:
2025-07-01 03:03:03.995 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:03.999 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:04.004 return
2025-07-01 03:03:04.008 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:04.013 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:04.017 else:
2025-07-01 03:03:04.023 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:04.029 eqi = None
2025-07-01 03:03:04.034
2025-07-01 03:03:04.039 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:04.044 # identical
2025-07-01 03:03:04.050
2025-07-01 03:03:04.055 # pump out diffs from before the synch point
2025-07-01 03:03:04.060 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:04.066
2025-07-01 03:03:04.072 # do intraline marking on the synch pair
2025-07-01 03:03:04.077 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:04.083 if eqi is None:
2025-07-01 03:03:04.089 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:04.094 atags = btags = ""
2025-07-01 03:03:04.099 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:04.103 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:04.108 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:04.112 if tag == 'replace':
2025-07-01 03:03:04.118 atags += '^' * la
2025-07-01 03:03:04.123 btags += '^' * lb
2025-07-01 03:03:04.128 elif tag == 'delete':
2025-07-01 03:03:04.134 atags += '-' * la
2025-07-01 03:03:04.139 elif tag == 'insert':
2025-07-01 03:03:04.144 btags += '+' * lb
2025-07-01 03:03:04.150 elif tag == 'equal':
2025-07-01 03:03:04.155 atags += ' ' * la
2025-07-01 03:03:04.160 btags += ' ' * lb
2025-07-01 03:03:04.166 else:
2025-07-01 03:03:04.170 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:04.177 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:04.182 else:
2025-07-01 03:03:04.188 # the synch pair is identical
2025-07-01 03:03:04.193 yield '  ' + aelt
2025-07-01 03:03:04.197
2025-07-01 03:03:04.203 # pump out diffs from after the synch point
2025-07-01 03:03:04.208 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:04.212
2025-07-01 03:03:04.217 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:04.221 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:04.227
2025-07-01 03:03:04.232 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:04.238 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:04.243 alo = 285, ahi = 1101
2025-07-01 03:03:04.248 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:04.254 blo = 285, bhi = 1101
2025-07-01 03:03:04.259
2025-07-01 03:03:04.264 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:04.268 g = []
2025-07-01 03:03:04.274 if alo < ahi:
2025-07-01 03:03:04.279 if blo < bhi:
2025-07-01 03:03:04.283 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:04.289 else:
2025-07-01 03:03:04.294 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:04.300 elif blo < bhi:
2025-07-01 03:03:04.305 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:04.310
2025-07-01 03:03:04.314 >       yield from g
2025-07-01 03:03:04.318
2025-07-01 03:03:04.323 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:04.329 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:04.334
2025-07-01 03:03:04.339 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:04.344 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:04.349 alo = 285, ahi = 1101
2025-07-01 03:03:04.354 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:04.358 blo = 285, bhi = 1101
2025-07-01 03:03:04.362
2025-07-01 03:03:04.367 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:04.371 r"""
2025-07-01 03:03:04.376 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:04.380 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:04.385 synch point, and intraline difference marking is done on the
2025-07-01 03:03:04.390 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:04.396
2025-07-01 03:03:04.401 Example:
2025-07-01 03:03:04.405
2025-07-01 03:03:04.410 >>> d = Differ()
2025-07-01 03:03:04.414 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:04.418 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:04.423 >>> print(''.join(results), end="")
2025-07-01 03:03:04.428 - abcDefghiJkl
2025-07-01 03:03:04.437 + abcdefGhijkl
2025-07-01 03:03:04.446 """
2025-07-01 03:03:04.451
2025-07-01 03:03:04.456 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:04.461 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:04.465 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:04.470 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:04.475 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:04.479
2025-07-01 03:03:04.484 # search for the pair that matches best without being identical
2025-07-01 03:03:04.489 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:04.493 # on junk -- unless we have to)
2025-07-01 03:03:04.498 for j in range(blo, bhi):
2025-07-01 03:03:04.503 bj = b[j]
2025-07-01 03:03:04.507 cruncher.set_seq2(bj)
2025-07-01 03:03:04.512 for i in range(alo, ahi):
2025-07-01 03:03:04.517 ai = a[i]
2025-07-01 03:03:04.522 if ai == bj:
2025-07-01 03:03:04.526 if eqi is None:
2025-07-01 03:03:04.531 eqi, eqj = i, j
2025-07-01 03:03:04.535 continue
2025-07-01 03:03:04.540 cruncher.set_seq1(ai)
2025-07-01 03:03:04.544 # computing similarity is expensive, so use the quick
2025-07-01 03:03:04.549 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:04.554 # compares by a factor of 3.
2025-07-01 03:03:04.558 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:04.563 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:04.567 # of the computation is cached by cruncher
2025-07-01 03:03:04.572 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:04.576 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:04.581 cruncher.ratio() > best_ratio:
2025-07-01 03:03:04.585 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:04.590 if best_ratio < cutoff:
2025-07-01 03:03:04.595 # no non-identical "pretty close" pair
2025-07-01 03:03:04.599 if eqi is None:
2025-07-01 03:03:04.604 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:04.608 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:04.613 return
2025-07-01 03:03:04.617 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:04.622 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:04.627 else:
2025-07-01 03:03:04.632 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:04.636 eqi = None
2025-07-01 03:03:04.641
2025-07-01 03:03:04.645 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:04.650 # identical
2025-07-01 03:03:04.654
2025-07-01 03:03:04.659 # pump out diffs from before the synch point
2025-07-01 03:03:04.665 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:04.671
2025-07-01 03:03:04.676 # do intraline marking on the synch pair
2025-07-01 03:03:04.681 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:04.686 if eqi is None:
2025-07-01 03:03:04.692 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:04.698 atags = btags = ""
2025-07-01 03:03:04.703 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:04.709 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:04.714 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:04.720 if tag == 'replace':
2025-07-01 03:03:04.725 atags += '^' * la
2025-07-01 03:03:04.730 btags += '^' * lb
2025-07-01 03:03:04.736 elif tag == 'delete':
2025-07-01 03:03:04.742 atags += '-' * la
2025-07-01 03:03:04.747 elif tag == 'insert':
2025-07-01 03:03:04.751 btags += '+' * lb
2025-07-01 03:03:04.757 elif tag == 'equal':
2025-07-01 03:03:04.762 atags += ' ' * la
2025-07-01 03:03:04.769 btags += ' ' * lb
2025-07-01 03:03:04.775 else:
2025-07-01 03:03:04.781 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:04.788 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:04.794 else:
2025-07-01 03:03:04.800 # the synch pair is identical
2025-07-01 03:03:04.806 yield '  ' + aelt
2025-07-01 03:03:04.811
2025-07-01 03:03:04.815 # pump out diffs from after the synch point
2025-07-01 03:03:04.820 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:04.825
2025-07-01 03:03:04.829 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:04.834 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:04.839
2025-07-01 03:03:04.843 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:04.848 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:04.853 alo = 286, ahi = 1101
2025-07-01 03:03:04.858 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:04.862 blo = 286, bhi = 1101
2025-07-01 03:03:04.866
2025-07-01 03:03:04.871 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:04.876 g = []
2025-07-01 03:03:04.881 if alo < ahi:
2025-07-01 03:03:04.886 if blo < bhi:
2025-07-01 03:03:04.891 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:04.895 else:
2025-07-01 03:03:04.900 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:04.904 elif blo < bhi:
2025-07-01 03:03:04.909 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:04.925
2025-07-01 03:03:04.930 >       yield from g
2025-07-01 03:03:04.937
2025-07-01 03:03:04.957 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:04.973 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:04.982
2025-07-01 03:03:04.997 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:05.017 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:05.031 alo = 286, ahi = 1101
2025-07-01 03:03:05.050 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:05.056 blo = 286, bhi = 1101
2025-07-01 03:03:05.061
2025-07-01 03:03:05.067 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:05.072 r"""
2025-07-01 03:03:05.077 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:05.083 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:05.089 synch point, and intraline difference marking is done on the
2025-07-01 03:03:05.094 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:05.099
2025-07-01 03:03:05.103 Example:
2025-07-01 03:03:05.107
2025-07-01 03:03:05.112 >>> d = Differ()
2025-07-01 03:03:05.116 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:05.121 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:05.127 >>> print(''.join(results), end="")
2025-07-01 03:03:05.134 - abcDefghiJkl
2025-07-01 03:03:05.145 + abcdefGhijkl
2025-07-01 03:03:05.154 """
2025-07-01 03:03:05.160
2025-07-01 03:03:05.165 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:05.170 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:05.176 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:05.181 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:05.186 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:05.192
2025-07-01 03:03:05.198 # search for the pair that matches best without being identical
2025-07-01 03:03:05.204 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:05.209 # on junk -- unless we have to)
2025-07-01 03:03:05.215 for j in range(blo, bhi):
2025-07-01 03:03:05.219 bj = b[j]
2025-07-01 03:03:05.224 cruncher.set_seq2(bj)
2025-07-01 03:03:05.230 for i in range(alo, ahi):
2025-07-01 03:03:05.235 ai = a[i]
2025-07-01 03:03:05.241 if ai == bj:
2025-07-01 03:03:05.247 if eqi is None:
2025-07-01 03:03:05.252 eqi, eqj = i, j
2025-07-01 03:03:05.258 continue
2025-07-01 03:03:05.262 cruncher.set_seq1(ai)
2025-07-01 03:03:05.267 # computing similarity is expensive, so use the quick
2025-07-01 03:03:05.272 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:05.277 # compares by a factor of 3.
2025-07-01 03:03:05.283 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:05.287 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:05.292 # of the computation is cached by cruncher
2025-07-01 03:03:05.298 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:05.303 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:05.309 cruncher.ratio() > best_ratio:
2025-07-01 03:03:05.314 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:05.318 if best_ratio < cutoff:
2025-07-01 03:03:05.323 # no non-identical "pretty close" pair
2025-07-01 03:03:05.329 if eqi is None:
2025-07-01 03:03:05.334 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:05.339 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:05.344 return
2025-07-01 03:03:05.350 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:05.356 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:05.361 else:
2025-07-01 03:03:05.366 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:05.370 eqi = None
2025-07-01 03:03:05.374
2025-07-01 03:03:05.378 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:05.383 # identical
2025-07-01 03:03:05.387
2025-07-01 03:03:05.391 # pump out diffs from before the synch point
2025-07-01 03:03:05.396 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:05.400
2025-07-01 03:03:05.405 # do intraline marking on the synch pair
2025-07-01 03:03:05.409 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:05.413 if eqi is None:
2025-07-01 03:03:05.418 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:05.422 atags = btags = ""
2025-07-01 03:03:05.426 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:05.431 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:05.437 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:05.443 if tag == 'replace':
2025-07-01 03:03:05.448 atags += '^' * la
2025-07-01 03:03:05.454 btags += '^' * lb
2025-07-01 03:03:05.460 elif tag == 'delete':
2025-07-01 03:03:05.465 atags += '-' * la
2025-07-01 03:03:05.470 elif tag == 'insert':
2025-07-01 03:03:05.476 btags += '+' * lb
2025-07-01 03:03:05.481 elif tag == 'equal':
2025-07-01 03:03:05.486 atags += ' ' * la
2025-07-01 03:03:05.491 btags += ' ' * lb
2025-07-01 03:03:05.496 else:
2025-07-01 03:03:05.502 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:05.507 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:05.512 else:
2025-07-01 03:03:05.517 # the synch pair is identical
2025-07-01 03:03:05.522 yield '  ' + aelt
2025-07-01 03:03:05.534
2025-07-01 03:03:05.543 # pump out diffs from after the synch point
2025-07-01 03:03:05.551 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:05.556
2025-07-01 03:03:05.561 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:05.565 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:05.570
2025-07-01 03:03:05.576 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:05.581 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:05.586 alo = 287, ahi = 1101
2025-07-01 03:03:05.591 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:05.596 blo = 287, bhi = 1101
2025-07-01 03:03:05.600
2025-07-01 03:03:05.605 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:05.609 g = []
2025-07-01 03:03:05.614 if alo < ahi:
2025-07-01 03:03:05.619 if blo < bhi:
2025-07-01 03:03:05.623 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:05.627 else:
2025-07-01 03:03:05.632 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:05.637 elif blo < bhi:
2025-07-01 03:03:05.641 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:05.646
2025-07-01 03:03:05.650 >       yield from g
2025-07-01 03:03:05.654
2025-07-01 03:03:05.659 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:05.663 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:05.669
2025-07-01 03:03:05.674 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:05.679 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:05.683 alo = 287, ahi = 1101
2025-07-01 03:03:05.689 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:05.694 blo = 287, bhi = 1101
2025-07-01 03:03:05.698
2025-07-01 03:03:05.703 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:05.707 r"""
2025-07-01 03:03:05.712 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:05.716 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:05.720 synch point, and intraline difference marking is done on the
2025-07-01 03:03:05.725 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:05.729
2025-07-01 03:03:05.734 Example:
2025-07-01 03:03:05.738
2025-07-01 03:03:05.743 >>> d = Differ()
2025-07-01 03:03:05.747 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:05.752 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:05.756 >>> print(''.join(results), end="")
2025-07-01 03:03:05.761 - abcDefghiJkl
2025-07-01 03:03:05.770 + abcdefGhijkl
2025-07-01 03:03:05.779 """
2025-07-01 03:03:05.784
2025-07-01 03:03:05.788 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:05.793 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:05.797 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:05.802 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:05.807 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:05.811
2025-07-01 03:03:05.816 # search for the pair that matches best without being identical
2025-07-01 03:03:05.820 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:05.825 # on junk -- unless we have to)
2025-07-01 03:03:05.829 for j in range(blo, bhi):
2025-07-01 03:03:05.834 bj = b[j]
2025-07-01 03:03:05.839 cruncher.set_seq2(bj)
2025-07-01 03:03:05.844 for i in range(alo, ahi):
2025-07-01 03:03:05.848 ai = a[i]
2025-07-01 03:03:05.853 if ai == bj:
2025-07-01 03:03:05.857 if eqi is None:
2025-07-01 03:03:05.862 eqi, eqj = i, j
2025-07-01 03:03:05.867 continue
2025-07-01 03:03:05.871 cruncher.set_seq1(ai)
2025-07-01 03:03:05.876 # computing similarity is expensive, so use the quick
2025-07-01 03:03:05.881 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:05.886 # compares by a factor of 3.
2025-07-01 03:03:05.891 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:05.896 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:05.901 # of the computation is cached by cruncher
2025-07-01 03:03:05.905 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:05.911 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:05.917 cruncher.ratio() > best_ratio:
2025-07-01 03:03:05.921 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:05.926 if best_ratio < cutoff:
2025-07-01 03:03:05.931 # no non-identical "pretty close" pair
2025-07-01 03:03:05.935 if eqi is None:
2025-07-01 03:03:05.940 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:05.945 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:05.949 return
2025-07-01 03:03:05.954 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:05.960 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:05.966 else:
2025-07-01 03:03:05.971 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:05.975 eqi = None
2025-07-01 03:03:05.980
2025-07-01 03:03:05.985 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:05.991 # identical
2025-07-01 03:03:05.997
2025-07-01 03:03:06.003 # pump out diffs from before the synch point
2025-07-01 03:03:06.010 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:06.015
2025-07-01 03:03:06.021 # do intraline marking on the synch pair
2025-07-01 03:03:06.027 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:06.033 if eqi is None:
2025-07-01 03:03:06.040 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:06.045 atags = btags = ""
2025-07-01 03:03:06.050 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:06.056 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:06.062 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:06.067 if tag == 'replace':
2025-07-01 03:03:06.072 atags += '^' * la
2025-07-01 03:03:06.078 btags += '^' * lb
2025-07-01 03:03:06.083 elif tag == 'delete':
2025-07-01 03:03:06.088 atags += '-' * la
2025-07-01 03:03:06.094 elif tag == 'insert':
2025-07-01 03:03:06.099 btags += '+' * lb
2025-07-01 03:03:06.104 elif tag == 'equal':
2025-07-01 03:03:06.110 atags += ' ' * la
2025-07-01 03:03:06.115 btags += ' ' * lb
2025-07-01 03:03:06.121 else:
2025-07-01 03:03:06.127 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:06.133 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:06.139 else:
2025-07-01 03:03:06.144 # the synch pair is identical
2025-07-01 03:03:06.148 yield '  ' + aelt
2025-07-01 03:03:06.154
2025-07-01 03:03:06.159 # pump out diffs from after the synch point
2025-07-01 03:03:06.164 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:06.170
2025-07-01 03:03:06.176 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:06.181 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:06.186
2025-07-01 03:03:06.192 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:06.198 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:06.204 alo = 290, ahi = 1101
2025-07-01 03:03:06.210 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:06.216 blo = 290, bhi = 1101
2025-07-01 03:03:06.221
2025-07-01 03:03:06.227 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:06.232 g = []
2025-07-01 03:03:06.238 if alo < ahi:
2025-07-01 03:03:06.243 if blo < bhi:
2025-07-01 03:03:06.247 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:06.252 else:
2025-07-01 03:03:06.257 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:06.261 elif blo < bhi:
2025-07-01 03:03:06.266 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:06.270
2025-07-01 03:03:06.275 >       yield from g
2025-07-01 03:03:06.280
2025-07-01 03:03:06.285 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:06.290 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:06.294
2025-07-01 03:03:06.299 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:06.304 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:06.309 alo = 290, ahi = 1101
2025-07-01 03:03:06.314 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:06.319 blo = 290, bhi = 1101
2025-07-01 03:03:06.323
2025-07-01 03:03:06.328 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:06.333 r"""
2025-07-01 03:03:06.337 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:06.342 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:06.348 synch point, and intraline difference marking is done on the
2025-07-01 03:03:06.352 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:06.357
2025-07-01 03:03:06.362 Example:
2025-07-01 03:03:06.367
2025-07-01 03:03:06.371 >>> d = Differ()
2025-07-01 03:03:06.376 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:06.381 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:06.386 >>> print(''.join(results), end="")
2025-07-01 03:03:06.392 - abcDefghiJkl
2025-07-01 03:03:06.402 + abcdefGhijkl
2025-07-01 03:03:06.411 """
2025-07-01 03:03:06.415
2025-07-01 03:03:06.420 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:06.424 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:06.429 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:06.434 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:06.438 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:06.443
2025-07-01 03:03:06.447 # search for the pair that matches best without being identical
2025-07-01 03:03:06.452 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:06.456 # on junk -- unless we have to)
2025-07-01 03:03:06.461 for j in range(blo, bhi):
2025-07-01 03:03:06.465 bj = b[j]
2025-07-01 03:03:06.470 cruncher.set_seq2(bj)
2025-07-01 03:03:06.474 for i in range(alo, ahi):
2025-07-01 03:03:06.479 ai = a[i]
2025-07-01 03:03:06.483 if ai == bj:
2025-07-01 03:03:06.488 if eqi is None:
2025-07-01 03:03:06.492 eqi, eqj = i, j
2025-07-01 03:03:06.497 continue
2025-07-01 03:03:06.502 cruncher.set_seq1(ai)
2025-07-01 03:03:06.507 # computing similarity is expensive, so use the quick
2025-07-01 03:03:06.512 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:06.517 # compares by a factor of 3.
2025-07-01 03:03:06.522 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:06.526 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:06.531 # of the computation is cached by cruncher
2025-07-01 03:03:06.535 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:06.540 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:06.544 cruncher.ratio() > best_ratio:
2025-07-01 03:03:06.549 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:06.554 if best_ratio < cutoff:
2025-07-01 03:03:06.559 # no non-identical "pretty close" pair
2025-07-01 03:03:06.563 if eqi is None:
2025-07-01 03:03:06.568 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:06.573 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:06.577 return
2025-07-01 03:03:06.582 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:06.587 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:06.592 else:
2025-07-01 03:03:06.596 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:06.601 eqi = None
2025-07-01 03:03:06.606
2025-07-01 03:03:06.610 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:06.615 # identical
2025-07-01 03:03:06.620
2025-07-01 03:03:06.625 # pump out diffs from before the synch point
2025-07-01 03:03:06.630 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:06.634
2025-07-01 03:03:06.639 # do intraline marking on the synch pair
2025-07-01 03:03:06.643 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:06.648 if eqi is None:
2025-07-01 03:03:06.653 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:06.657 atags = btags = ""
2025-07-01 03:03:06.662 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:06.666 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:06.671 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:06.675 if tag == 'replace':
2025-07-01 03:03:06.680 atags += '^' * la
2025-07-01 03:03:06.684 btags += '^' * lb
2025-07-01 03:03:06.689 elif tag == 'delete':
2025-07-01 03:03:06.693 atags += '-' * la
2025-07-01 03:03:06.698 elif tag == 'insert':
2025-07-01 03:03:06.702 btags += '+' * lb
2025-07-01 03:03:06.706 elif tag == 'equal':
2025-07-01 03:03:06.711 atags += ' ' * la
2025-07-01 03:03:06.715 btags += ' ' * lb
2025-07-01 03:03:06.720 else:
2025-07-01 03:03:06.724 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:06.729 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:06.734 else:
2025-07-01 03:03:06.738 # the synch pair is identical
2025-07-01 03:03:06.743 yield '  ' + aelt
2025-07-01 03:03:06.747
2025-07-01 03:03:06.752 # pump out diffs from after the synch point
2025-07-01 03:03:06.757 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:06.761
2025-07-01 03:03:06.765 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:06.770 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:06.774
2025-07-01 03:03:06.779 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:06.784 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:06.788 alo = 291, ahi = 1101
2025-07-01 03:03:06.793 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:06.797 blo = 291, bhi = 1101
2025-07-01 03:03:06.802
2025-07-01 03:03:06.806 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:06.811 g = []
2025-07-01 03:03:06.815 if alo < ahi:
2025-07-01 03:03:06.820 if blo < bhi:
2025-07-01 03:03:06.824 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:06.829 else:
2025-07-01 03:03:06.833 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:06.838 elif blo < bhi:
2025-07-01 03:03:06.842 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:06.846
2025-07-01 03:03:06.851 >       yield from g
2025-07-01 03:03:06.855
2025-07-01 03:03:06.860 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:06.864 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:06.870
2025-07-01 03:03:06.874 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:06.879 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:06.883 alo = 291, ahi = 1101
2025-07-01 03:03:06.888 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:06.893 blo = 291, bhi = 1101
2025-07-01 03:03:06.898
2025-07-01 03:03:06.902 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:06.907 r"""
2025-07-01 03:03:06.912 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:06.916 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:06.921 synch point, and intraline difference marking is done on the
2025-07-01 03:03:06.925 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:06.929
2025-07-01 03:03:06.934 Example:
2025-07-01 03:03:06.939
2025-07-01 03:03:06.944 >>> d = Differ()
2025-07-01 03:03:06.949 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:06.953 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:06.958 >>> print(''.join(results), end="")
2025-07-01 03:03:06.962 - abcDefghiJkl
2025-07-01 03:03:06.971 + abcdefGhijkl
2025-07-01 03:03:06.980 """
2025-07-01 03:03:06.984
2025-07-01 03:03:06.989 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:06.994 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:06.998 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:07.003 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:07.007 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:07.012
2025-07-01 03:03:07.017 # search for the pair that matches best without being identical
2025-07-01 03:03:07.021 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:07.026 # on junk -- unless we have to)
2025-07-01 03:03:07.030 for j in range(blo, bhi):
2025-07-01 03:03:07.035 bj = b[j]
2025-07-01 03:03:07.039 cruncher.set_seq2(bj)
2025-07-01 03:03:07.044 for i in range(alo, ahi):
2025-07-01 03:03:07.049 ai = a[i]
2025-07-01 03:03:07.053 if ai == bj:
2025-07-01 03:03:07.058 if eqi is None:
2025-07-01 03:03:07.062 eqi, eqj = i, j
2025-07-01 03:03:07.067 continue
2025-07-01 03:03:07.071 cruncher.set_seq1(ai)
2025-07-01 03:03:07.076 # computing similarity is expensive, so use the quick
2025-07-01 03:03:07.080 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:07.084 # compares by a factor of 3.
2025-07-01 03:03:07.089 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:07.093 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:07.098 # of the computation is cached by cruncher
2025-07-01 03:03:07.102 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:07.107 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:07.111 cruncher.ratio() > best_ratio:
2025-07-01 03:03:07.116 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:07.120 if best_ratio < cutoff:
2025-07-01 03:03:07.125 # no non-identical "pretty close" pair
2025-07-01 03:03:07.129 if eqi is None:
2025-07-01 03:03:07.133 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:07.138 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:07.142 return
2025-07-01 03:03:07.147 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:07.152 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:07.157 else:
2025-07-01 03:03:07.161 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:07.166 eqi = None
2025-07-01 03:03:07.170
2025-07-01 03:03:07.176 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:07.180 # identical
2025-07-01 03:03:07.185
2025-07-01 03:03:07.189 # pump out diffs from before the synch point
2025-07-01 03:03:07.193 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:07.198
2025-07-01 03:03:07.202 # do intraline marking on the synch pair
2025-07-01 03:03:07.207 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:07.211 if eqi is None:
2025-07-01 03:03:07.216 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:07.220 atags = btags = ""
2025-07-01 03:03:07.225 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:07.230 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:07.234 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:07.238 if tag == 'replace':
2025-07-01 03:03:07.243 atags += '^' * la
2025-07-01 03:03:07.247 btags += '^' * lb
2025-07-01 03:03:07.251 elif tag == 'delete':
2025-07-01 03:03:07.256 atags += '-' * la
2025-07-01 03:03:07.260 elif tag == 'insert':
2025-07-01 03:03:07.265 btags += '+' * lb
2025-07-01 03:03:07.269 elif tag == 'equal':
2025-07-01 03:03:07.273 atags += ' ' * la
2025-07-01 03:03:07.278 btags += ' ' * lb
2025-07-01 03:03:07.282 else:
2025-07-01 03:03:07.287 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:07.291 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:07.295 else:
2025-07-01 03:03:07.300 # the synch pair is identical
2025-07-01 03:03:07.305 yield '  ' + aelt
2025-07-01 03:03:07.309
2025-07-01 03:03:07.313 # pump out diffs from after the synch point
2025-07-01 03:03:07.318 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:07.322
2025-07-01 03:03:07.327 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:07.331 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:07.336
2025-07-01 03:03:07.340 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:07.345 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:07.350 alo = 292, ahi = 1101
2025-07-01 03:03:07.355 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:07.360 blo = 292, bhi = 1101
2025-07-01 03:03:07.365
2025-07-01 03:03:07.369 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:07.374 g = []
2025-07-01 03:03:07.379 if alo < ahi:
2025-07-01 03:03:07.384 if blo < bhi:
2025-07-01 03:03:07.388 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:07.393 else:
2025-07-01 03:03:07.397 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:07.402 elif blo < bhi:
2025-07-01 03:03:07.406 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:07.411
2025-07-01 03:03:07.416 >       yield from g
2025-07-01 03:03:07.421
2025-07-01 03:03:07.426 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:07.430 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:07.435
2025-07-01 03:03:07.440 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:07.445 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:07.450 alo = 292, ahi = 1101
2025-07-01 03:03:07.455 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:07.460 blo = 292, bhi = 1101
2025-07-01 03:03:07.464
2025-07-01 03:03:07.469 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:07.473 r"""
2025-07-01 03:03:07.478 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:07.483 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:07.488 synch point, and intraline difference marking is done on the
2025-07-01 03:03:07.493 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:07.498
2025-07-01 03:03:07.503 Example:
2025-07-01 03:03:07.507
2025-07-01 03:03:07.512 >>> d = Differ()
2025-07-01 03:03:07.517 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:07.522 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:07.527 >>> print(''.join(results), end="")
2025-07-01 03:03:07.531 - abcDefghiJkl
2025-07-01 03:03:07.541 + abcdefGhijkl
2025-07-01 03:03:07.551 """
2025-07-01 03:03:07.555
2025-07-01 03:03:07.559 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:07.564 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:07.568 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:07.573 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:07.577 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:07.581
2025-07-01 03:03:07.586 # search for the pair that matches best without being identical
2025-07-01 03:03:07.591 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:07.595 # on junk -- unless we have to)
2025-07-01 03:03:07.600 for j in range(blo, bhi):
2025-07-01 03:03:07.604 bj = b[j]
2025-07-01 03:03:07.608 cruncher.set_seq2(bj)
2025-07-01 03:03:07.613 for i in range(alo, ahi):
2025-07-01 03:03:07.618 ai = a[i]
2025-07-01 03:03:07.622 if ai == bj:
2025-07-01 03:03:07.627 if eqi is None:
2025-07-01 03:03:07.632 eqi, eqj = i, j
2025-07-01 03:03:07.637 continue
2025-07-01 03:03:07.641 cruncher.set_seq1(ai)
2025-07-01 03:03:07.646 # computing similarity is expensive, so use the quick
2025-07-01 03:03:07.651 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:07.658 # compares by a factor of 3.
2025-07-01 03:03:07.664 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:07.670 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:07.675 # of the computation is cached by cruncher
2025-07-01 03:03:07.680 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:07.685 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:07.690 cruncher.ratio() > best_ratio:
2025-07-01 03:03:07.695 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:07.699 if best_ratio < cutoff:
2025-07-01 03:03:07.704 # no non-identical "pretty close" pair
2025-07-01 03:03:07.709 if eqi is None:
2025-07-01 03:03:07.713 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:07.718 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:07.722 return
2025-07-01 03:03:07.728 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:07.733 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:07.738 else:
2025-07-01 03:03:07.744 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:07.750 eqi = None
2025-07-01 03:03:07.757
2025-07-01 03:03:07.764 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:07.770 # identical
2025-07-01 03:03:07.777
2025-07-01 03:03:07.783 # pump out diffs from before the synch point
2025-07-01 03:03:07.789 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:07.795
2025-07-01 03:03:07.801 # do intraline marking on the synch pair
2025-07-01 03:03:07.805 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:07.810 if eqi is None:
2025-07-01 03:03:07.814 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:07.818 atags = btags = ""
2025-07-01 03:03:07.823 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:07.828 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:07.832 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:07.836 if tag == 'replace':
2025-07-01 03:03:07.841 atags += '^' * la
2025-07-01 03:03:07.845 btags += '^' * lb
2025-07-01 03:03:07.850 elif tag == 'delete':
2025-07-01 03:03:07.854 atags += '-' * la
2025-07-01 03:03:07.858 elif tag == 'insert':
2025-07-01 03:03:07.863 btags += '+' * lb
2025-07-01 03:03:07.867 elif tag == 'equal':
2025-07-01 03:03:07.871 atags += ' ' * la
2025-07-01 03:03:07.876 btags += ' ' * lb
2025-07-01 03:03:07.880 else:
2025-07-01 03:03:07.884 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:07.889 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:07.893 else:
2025-07-01 03:03:07.897 # the synch pair is identical
2025-07-01 03:03:07.901 yield '  ' + aelt
2025-07-01 03:03:07.905
2025-07-01 03:03:07.910 # pump out diffs from after the synch point
2025-07-01 03:03:07.915 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:07.919
2025-07-01 03:03:07.924 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:07.928 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:07.932
2025-07-01 03:03:07.937 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:07.941 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:07.946 alo = 293, ahi = 1101
2025-07-01 03:03:07.951 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:07.955 blo = 293, bhi = 1101
2025-07-01 03:03:07.959
2025-07-01 03:03:07.963 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:07.968 g = []
2025-07-01 03:03:07.973 if alo < ahi:
2025-07-01 03:03:07.977 if blo < bhi:
2025-07-01 03:03:07.982 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:07.986 else:
2025-07-01 03:03:07.990 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:07.994 elif blo < bhi:
2025-07-01 03:03:07.999 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:08.003
2025-07-01 03:03:08.007 >       yield from g
2025-07-01 03:03:08.012
2025-07-01 03:03:08.016 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:08.021 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:08.025
2025-07-01 03:03:08.029 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:08.034 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:08.038 alo = 293, ahi = 1101
2025-07-01 03:03:08.043 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:08.047 blo = 293, bhi = 1101
2025-07-01 03:03:08.051
2025-07-01 03:03:08.056 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:08.060 r"""
2025-07-01 03:03:08.064 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:08.069 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:08.073 synch point, and intraline difference marking is done on the
2025-07-01 03:03:08.077 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:08.082
2025-07-01 03:03:08.086 Example:
2025-07-01 03:03:08.090
2025-07-01 03:03:08.094 >>> d = Differ()
2025-07-01 03:03:08.098 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:08.103 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:08.107 >>> print(''.join(results), end="")
2025-07-01 03:03:08.112 - abcDefghiJkl
2025-07-01 03:03:08.120 + abcdefGhijkl
2025-07-01 03:03:08.129 """
2025-07-01 03:03:08.133
2025-07-01 03:03:08.137 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:08.142 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:08.146 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:08.151 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:08.155 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:08.159
2025-07-01 03:03:08.164 # search for the pair that matches best without being identical
2025-07-01 03:03:08.168 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:08.173 # on junk -- unless we have to)
2025-07-01 03:03:08.177 for j in range(blo, bhi):
2025-07-01 03:03:08.181 bj = b[j]
2025-07-01 03:03:08.185 cruncher.set_seq2(bj)
2025-07-01 03:03:08.190 for i in range(alo, ahi):
2025-07-01 03:03:08.194 ai = a[i]
2025-07-01 03:03:08.198 if ai == bj:
2025-07-01 03:03:08.202 if eqi is None:
2025-07-01 03:03:08.207 eqi, eqj = i, j
2025-07-01 03:03:08.211 continue
2025-07-01 03:03:08.215 cruncher.set_seq1(ai)
2025-07-01 03:03:08.220 # computing similarity is expensive, so use the quick
2025-07-01 03:03:08.224 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:08.228 # compares by a factor of 3.
2025-07-01 03:03:08.232 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:08.237 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:08.241 # of the computation is cached by cruncher
2025-07-01 03:03:08.245 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:08.250 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:08.254 cruncher.ratio() > best_ratio:
2025-07-01 03:03:08.258 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:08.263 if best_ratio < cutoff:
2025-07-01 03:03:08.267 # no non-identical "pretty close" pair
2025-07-01 03:03:08.271 if eqi is None:
2025-07-01 03:03:08.276 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:08.280 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:08.284 return
2025-07-01 03:03:08.289 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:08.294 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:08.298 else:
2025-07-01 03:03:08.302 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:08.306 eqi = None
2025-07-01 03:03:08.311
2025-07-01 03:03:08.315 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:08.319 # identical
2025-07-01 03:03:08.324
2025-07-01 03:03:08.328 # pump out diffs from before the synch point
2025-07-01 03:03:08.333 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:08.337
2025-07-01 03:03:08.341 # do intraline marking on the synch pair
2025-07-01 03:03:08.345 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:08.350 if eqi is None:
2025-07-01 03:03:08.354 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:08.358 atags = btags = ""
2025-07-01 03:03:08.363 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:08.367 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:08.372 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:08.377 if tag == 'replace':
2025-07-01 03:03:08.382 atags += '^' * la
2025-07-01 03:03:08.386 btags += '^' * lb
2025-07-01 03:03:08.390 elif tag == 'delete':
2025-07-01 03:03:08.395 atags += '-' * la
2025-07-01 03:03:08.399 elif tag == 'insert':
2025-07-01 03:03:08.404 btags += '+' * lb
2025-07-01 03:03:08.409 elif tag == 'equal':
2025-07-01 03:03:08.413 atags += ' ' * la
2025-07-01 03:03:08.417 btags += ' ' * lb
2025-07-01 03:03:08.422 else:
2025-07-01 03:03:08.426 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:08.431 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:08.435 else:
2025-07-01 03:03:08.439 # the synch pair is identical
2025-07-01 03:03:08.444 yield '  ' + aelt
2025-07-01 03:03:08.448
2025-07-01 03:03:08.452 # pump out diffs from after the synch point
2025-07-01 03:03:08.457 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:08.461
2025-07-01 03:03:08.465 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:08.470 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:08.474
2025-07-01 03:03:08.478 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:08.483 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:08.488 alo = 294, ahi = 1101
2025-07-01 03:03:08.492 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:08.497 blo = 294, bhi = 1101
2025-07-01 03:03:08.501
2025-07-01 03:03:08.506 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:08.511 g = []
2025-07-01 03:03:08.516 if alo < ahi:
2025-07-01 03:03:08.521 if blo < bhi:
2025-07-01 03:03:08.526 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:08.531 else:
2025-07-01 03:03:08.536 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:08.541 elif blo < bhi:
2025-07-01 03:03:08.546 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:08.551
2025-07-01 03:03:08.556 >       yield from g
2025-07-01 03:03:08.561
2025-07-01 03:03:08.566 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:08.571 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:08.576
2025-07-01 03:03:08.581 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:08.586 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:08.591 alo = 294, ahi = 1101
2025-07-01 03:03:08.597 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:08.601 blo = 294, bhi = 1101
2025-07-01 03:03:08.606
2025-07-01 03:03:08.611 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:08.615 r"""
2025-07-01 03:03:08.620 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:08.624 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:08.629 synch point, and intraline difference marking is done on the
2025-07-01 03:03:08.634 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:08.638
2025-07-01 03:03:08.643 Example:
2025-07-01 03:03:08.647
2025-07-01 03:03:08.651 >>> d = Differ()
2025-07-01 03:03:08.655 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:08.660 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:08.664 >>> print(''.join(results), end="")
2025-07-01 03:03:08.669 - abcDefghiJkl
2025-07-01 03:03:08.677 + abcdefGhijkl
2025-07-01 03:03:08.686 """
2025-07-01 03:03:08.690
2025-07-01 03:03:08.694 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:08.699 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:08.703 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:08.708 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:08.712 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:08.716
2025-07-01 03:03:08.721 # search for the pair that matches best without being identical
2025-07-01 03:03:08.725 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:08.730 # on junk -- unless we have to)
2025-07-01 03:03:08.734 for j in range(blo, bhi):
2025-07-01 03:03:08.739 bj = b[j]
2025-07-01 03:03:08.743 cruncher.set_seq2(bj)
2025-07-01 03:03:08.748 for i in range(alo, ahi):
2025-07-01 03:03:08.752 ai = a[i]
2025-07-01 03:03:08.756 if ai == bj:
2025-07-01 03:03:08.761 if eqi is None:
2025-07-01 03:03:08.765 eqi, eqj = i, j
2025-07-01 03:03:08.770 continue
2025-07-01 03:03:08.774 cruncher.set_seq1(ai)
2025-07-01 03:03:08.779 # computing similarity is expensive, so use the quick
2025-07-01 03:03:08.784 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:08.788 # compares by a factor of 3.
2025-07-01 03:03:08.792 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:08.797 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:08.802 # of the computation is cached by cruncher
2025-07-01 03:03:08.807 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:08.811 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:08.815 cruncher.ratio() > best_ratio:
2025-07-01 03:03:08.820 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:08.824 if best_ratio < cutoff:
2025-07-01 03:03:08.828 # no non-identical "pretty close" pair
2025-07-01 03:03:08.832 if eqi is None:
2025-07-01 03:03:08.837 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:08.842 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:08.846 return
2025-07-01 03:03:08.850 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:08.855 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:08.859 else:
2025-07-01 03:03:08.864 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:08.868 eqi = None
2025-07-01 03:03:08.872
2025-07-01 03:03:08.877 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:08.881 # identical
2025-07-01 03:03:08.885
2025-07-01 03:03:08.890 # pump out diffs from before the synch point
2025-07-01 03:03:08.894 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:08.899
2025-07-01 03:03:08.904 # do intraline marking on the synch pair
2025-07-01 03:03:08.908 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:08.913 if eqi is None:
2025-07-01 03:03:08.917 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:08.921 atags = btags = ""
2025-07-01 03:03:08.926 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:08.930 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:08.934 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:08.938 if tag == 'replace':
2025-07-01 03:03:08.943 atags += '^' * la
2025-07-01 03:03:08.947 btags += '^' * lb
2025-07-01 03:03:08.952 elif tag == 'delete':
2025-07-01 03:03:08.956 atags += '-' * la
2025-07-01 03:03:08.960 elif tag == 'insert':
2025-07-01 03:03:08.965 btags += '+' * lb
2025-07-01 03:03:08.969 elif tag == 'equal':
2025-07-01 03:03:08.973 atags += ' ' * la
2025-07-01 03:03:08.978 btags += ' ' * lb
2025-07-01 03:03:08.982 else:
2025-07-01 03:03:08.986 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:08.991 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:08.997 else:
2025-07-01 03:03:09.003 # the synch pair is identical
2025-07-01 03:03:09.007 yield '  ' + aelt
2025-07-01 03:03:09.011
2025-07-01 03:03:09.016 # pump out diffs from after the synch point
2025-07-01 03:03:09.021 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:09.025
2025-07-01 03:03:09.029 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:09.034 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:09.038
2025-07-01 03:03:09.043 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:09.049 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:09.055 alo = 295, ahi = 1101
2025-07-01 03:03:09.060 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:09.066 blo = 295, bhi = 1101
2025-07-01 03:03:09.070
2025-07-01 03:03:09.075 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:09.079 g = []
2025-07-01 03:03:09.084 if alo < ahi:
2025-07-01 03:03:09.090 if blo < bhi:
2025-07-01 03:03:09.096 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:09.101 else:
2025-07-01 03:03:09.106 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:09.111 elif blo < bhi:
2025-07-01 03:03:09.117 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:09.122
2025-07-01 03:03:09.127 >       yield from g
2025-07-01 03:03:09.132
2025-07-01 03:03:09.136 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:09.142 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:09.147
2025-07-01 03:03:09.151 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:09.157 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:09.163 alo = 295, ahi = 1101
2025-07-01 03:03:09.168 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:09.172 blo = 295, bhi = 1101
2025-07-01 03:03:09.177
2025-07-01 03:03:09.182 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:09.186 r"""
2025-07-01 03:03:09.192 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:09.202 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:09.216 synch point, and intraline difference marking is done on the
2025-07-01 03:03:09.222 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:09.228
2025-07-01 03:03:09.232 Example:
2025-07-01 03:03:09.237
2025-07-01 03:03:09.243 >>> d = Differ()
2025-07-01 03:03:09.248 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:09.254 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:09.260 >>> print(''.join(results), end="")
2025-07-01 03:03:09.264 - abcDefghiJkl
2025-07-01 03:03:09.273 + abcdefGhijkl
2025-07-01 03:03:09.281 """
2025-07-01 03:03:09.286
2025-07-01 03:03:09.291 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:09.295 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:09.300 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:09.305 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:09.310 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:09.314
2025-07-01 03:03:09.320 # search for the pair that matches best without being identical
2025-07-01 03:03:09.325 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:09.329 # on junk -- unless we have to)
2025-07-01 03:03:09.334 for j in range(blo, bhi):
2025-07-01 03:03:09.338 bj = b[j]
2025-07-01 03:03:09.342 cruncher.set_seq2(bj)
2025-07-01 03:03:09.347 for i in range(alo, ahi):
2025-07-01 03:03:09.351 ai = a[i]
2025-07-01 03:03:09.356 if ai == bj:
2025-07-01 03:03:09.361 if eqi is None:
2025-07-01 03:03:09.365 eqi, eqj = i, j
2025-07-01 03:03:09.370 continue
2025-07-01 03:03:09.375 cruncher.set_seq1(ai)
2025-07-01 03:03:09.381 # computing similarity is expensive, so use the quick
2025-07-01 03:03:09.386 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:09.391 # compares by a factor of 3.
2025-07-01 03:03:09.397 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:09.403 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:09.408 # of the computation is cached by cruncher
2025-07-01 03:03:09.413 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:09.418 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:09.423 cruncher.ratio() > best_ratio:
2025-07-01 03:03:09.429 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:09.434 if best_ratio < cutoff:
2025-07-01 03:03:09.438 # no non-identical "pretty close" pair
2025-07-01 03:03:09.443 if eqi is None:
2025-07-01 03:03:09.448 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:09.453 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:09.458 return
2025-07-01 03:03:09.463 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:09.467 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:09.472 else:
2025-07-01 03:03:09.478 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:09.482 eqi = None
2025-07-01 03:03:09.488
2025-07-01 03:03:09.493 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:09.499 # identical
2025-07-01 03:03:09.503
2025-07-01 03:03:09.508 # pump out diffs from before the synch point
2025-07-01 03:03:09.513 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:09.517
2025-07-01 03:03:09.523 # do intraline marking on the synch pair
2025-07-01 03:03:09.529 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:09.534 if eqi is None:
2025-07-01 03:03:09.539 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:09.544 atags = btags = ""
2025-07-01 03:03:09.548 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:09.553 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:09.558 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:09.564 if tag == 'replace':
2025-07-01 03:03:09.569 atags += '^' * la
2025-07-01 03:03:09.574 btags += '^' * lb
2025-07-01 03:03:09.579 elif tag == 'delete':
2025-07-01 03:03:09.584 atags += '-' * la
2025-07-01 03:03:09.590 elif tag == 'insert':
2025-07-01 03:03:09.595 btags += '+' * lb
2025-07-01 03:03:09.599 elif tag == 'equal':
2025-07-01 03:03:09.605 atags += ' ' * la
2025-07-01 03:03:09.610 btags += ' ' * lb
2025-07-01 03:03:09.615 else:
2025-07-01 03:03:09.627 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:09.634 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:09.640 else:
2025-07-01 03:03:09.645 # the synch pair is identical
2025-07-01 03:03:09.650 yield '  ' + aelt
2025-07-01 03:03:09.655
2025-07-01 03:03:09.661 # pump out diffs from after the synch point
2025-07-01 03:03:09.666 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:09.671
2025-07-01 03:03:09.676 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:09.681 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:09.687
2025-07-01 03:03:09.691 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:09.697 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:09.702 alo = 296, ahi = 1101
2025-07-01 03:03:09.708 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:09.713 blo = 296, bhi = 1101
2025-07-01 03:03:09.718
2025-07-01 03:03:09.723 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:09.728 g = []
2025-07-01 03:03:09.734 if alo < ahi:
2025-07-01 03:03:09.739 if blo < bhi:
2025-07-01 03:03:09.745 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:09.750 else:
2025-07-01 03:03:09.755 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:09.759 elif blo < bhi:
2025-07-01 03:03:09.765 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:09.770
2025-07-01 03:03:09.775 >       yield from g
2025-07-01 03:03:09.780
2025-07-01 03:03:09.785 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:09.791 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:09.795
2025-07-01 03:03:09.800 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:09.806 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:09.811 alo = 296, ahi = 1101
2025-07-01 03:03:09.816 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:09.821 blo = 296, bhi = 1101
2025-07-01 03:03:09.826
2025-07-01 03:03:09.831 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:09.836 r"""
2025-07-01 03:03:09.842 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:09.847 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:09.853 synch point, and intraline difference marking is done on the
2025-07-01 03:03:09.858 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:09.864
2025-07-01 03:03:09.869 Example:
2025-07-01 03:03:09.875
2025-07-01 03:03:09.881 >>> d = Differ()
2025-07-01 03:03:09.887 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:09.893 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:09.898 >>> print(''.join(results), end="")
2025-07-01 03:03:09.904 - abcDefghiJkl
2025-07-01 03:03:09.915 + abcdefGhijkl
2025-07-01 03:03:09.927 """
2025-07-01 03:03:09.932
2025-07-01 03:03:09.938 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:09.944 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:09.949 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:09.955 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:09.961 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:09.966
2025-07-01 03:03:09.970 # search for the pair that matches best without being identical
2025-07-01 03:03:09.974 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:09.979 # on junk -- unless we have to)
2025-07-01 03:03:09.983 for j in range(blo, bhi):
2025-07-01 03:03:09.987 bj = b[j]
2025-07-01 03:03:09.991 cruncher.set_seq2(bj)
2025-07-01 03:03:09.996 for i in range(alo, ahi):
2025-07-01 03:03:10.000 ai = a[i]
2025-07-01 03:03:10.004 if ai == bj:
2025-07-01 03:03:10.009 if eqi is None:
2025-07-01 03:03:10.013 eqi, eqj = i, j
2025-07-01 03:03:10.018 continue
2025-07-01 03:03:10.022 cruncher.set_seq1(ai)
2025-07-01 03:03:10.027 # computing similarity is expensive, so use the quick
2025-07-01 03:03:10.032 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:10.037 # compares by a factor of 3.
2025-07-01 03:03:10.044 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:10.049 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:10.053 # of the computation is cached by cruncher
2025-07-01 03:03:10.057 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:10.062 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:10.066 cruncher.ratio() > best_ratio:
2025-07-01 03:03:10.071 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:10.075 if best_ratio < cutoff:
2025-07-01 03:03:10.079 # no non-identical "pretty close" pair
2025-07-01 03:03:10.084 if eqi is None:
2025-07-01 03:03:10.093 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:10.098 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:10.103 return
2025-07-01 03:03:10.108 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:10.112 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:10.117 else:
2025-07-01 03:03:10.121 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:10.126 eqi = None
2025-07-01 03:03:10.131
2025-07-01 03:03:10.135 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:10.140 # identical
2025-07-01 03:03:10.146
2025-07-01 03:03:10.152 # pump out diffs from before the synch point
2025-07-01 03:03:10.157 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:10.161
2025-07-01 03:03:10.166 # do intraline marking on the synch pair
2025-07-01 03:03:10.186 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:10.197 if eqi is None:
2025-07-01 03:03:10.210 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:10.222 atags = btags = ""
2025-07-01 03:03:10.234 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:10.246 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:10.256 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:10.268 if tag == 'replace':
2025-07-01 03:03:10.278 atags += '^' * la
2025-07-01 03:03:10.294 btags += '^' * lb
2025-07-01 03:03:10.302 elif tag == 'delete':
2025-07-01 03:03:10.314 atags += '-' * la
2025-07-01 03:03:10.323 elif tag == 'insert':
2025-07-01 03:03:10.337 btags += '+' * lb
2025-07-01 03:03:10.346 elif tag == 'equal':
2025-07-01 03:03:10.362 atags += ' ' * la
2025-07-01 03:03:10.370 btags += ' ' * lb
2025-07-01 03:03:10.382 else:
2025-07-01 03:03:10.393 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:10.410 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:10.422 else:
2025-07-01 03:03:10.434 # the synch pair is identical
2025-07-01 03:03:10.454 yield '  ' + aelt
2025-07-01 03:03:10.470
2025-07-01 03:03:10.486 # pump out diffs from after the synch point
2025-07-01 03:03:10.502 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:10.514
2025-07-01 03:03:10.525 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:10.539 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:10.553
2025-07-01 03:03:10.562 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:10.578 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:10.594 alo = 297, ahi = 1101
2025-07-01 03:03:10.610 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:10.622 blo = 297, bhi = 1101
2025-07-01 03:03:10.633
2025-07-01 03:03:10.654 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:10.662 g = []
2025-07-01 03:03:10.673 if alo < ahi:
2025-07-01 03:03:10.682 if blo < bhi:
2025-07-01 03:03:10.698 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:10.705 else:
2025-07-01 03:03:10.722 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:10.733 elif blo < bhi:
2025-07-01 03:03:10.746 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:10.759
2025-07-01 03:03:10.777 >       yield from g
2025-07-01 03:03:10.790
2025-07-01 03:03:10.802 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:10.814 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:10.823
2025-07-01 03:03:10.837 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:10.849 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:10.857 alo = 297, ahi = 1101
2025-07-01 03:03:10.869 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:10.876 blo = 297, bhi = 1101
2025-07-01 03:03:10.881
2025-07-01 03:03:10.886 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:10.895 r"""
2025-07-01 03:03:10.913 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:10.926 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:10.938 synch point, and intraline difference marking is done on the
2025-07-01 03:03:10.953 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:10.964
2025-07-01 03:03:10.981 Example:
2025-07-01 03:03:10.995
2025-07-01 03:03:11.009 >>> d = Differ()
2025-07-01 03:03:11.020 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:11.029 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:11.046 >>> print(''.join(results), end="")
2025-07-01 03:03:11.058 - abcDefghiJkl
2025-07-01 03:03:11.078 + abcdefGhijkl
2025-07-01 03:03:11.102 """
2025-07-01 03:03:11.110
2025-07-01 03:03:11.120 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:11.134 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:11.146 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:11.158 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:11.167 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:11.174
2025-07-01 03:03:11.184 # search for the pair that matches best without being identical
2025-07-01 03:03:11.200 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:11.210 # on junk -- unless we have to)
2025-07-01 03:03:11.222 for j in range(blo, bhi):
2025-07-01 03:03:11.230 bj = b[j]
2025-07-01 03:03:11.246 cruncher.set_seq2(bj)
2025-07-01 03:03:11.254 for i in range(alo, ahi):
2025-07-01 03:03:11.266 ai = a[i]
2025-07-01 03:03:11.274 if ai == bj:
2025-07-01 03:03:11.286 if eqi is None:
2025-07-01 03:03:11.298 eqi, eqj = i, j
2025-07-01 03:03:11.310 continue
2025-07-01 03:03:11.318 cruncher.set_seq1(ai)
2025-07-01 03:03:11.332 # computing similarity is expensive, so use the quick
2025-07-01 03:03:11.342 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:11.358 # compares by a factor of 3.
2025-07-01 03:03:11.369 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:11.382 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:11.394 # of the computation is cached by cruncher
2025-07-01 03:03:11.404 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:11.414 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:11.422 cruncher.ratio() > best_ratio:
2025-07-01 03:03:11.430 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:11.442 if best_ratio < cutoff:
2025-07-01 03:03:11.456 # no non-identical "pretty close" pair
2025-07-01 03:03:11.466 if eqi is None:
2025-07-01 03:03:11.478 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:11.490 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:11.503 return
2025-07-01 03:03:11.522 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:11.535 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:11.550 else:
2025-07-01 03:03:11.559 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:11.578 eqi = None
2025-07-01 03:03:11.591
2025-07-01 03:03:11.606 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:11.618 # identical
2025-07-01 03:03:11.633
2025-07-01 03:03:11.646 # pump out diffs from before the synch point
2025-07-01 03:03:11.658 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:11.677
2025-07-01 03:03:11.690 # do intraline marking on the synch pair
2025-07-01 03:03:11.706 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:11.714 if eqi is None:
2025-07-01 03:03:11.722 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:11.734 atags = btags = ""
2025-07-01 03:03:11.746 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:11.757 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:11.770 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:11.778 if tag == 'replace':
2025-07-01 03:03:11.790 atags += '^' * la
2025-07-01 03:03:11.801 btags += '^' * lb
2025-07-01 03:03:11.813 elif tag == 'delete':
2025-07-01 03:03:11.832 atags += '-' * la
2025-07-01 03:03:11.842 elif tag == 'insert':
2025-07-01 03:03:11.854 btags += '+' * lb
2025-07-01 03:03:11.866 elif tag == 'equal':
2025-07-01 03:03:11.876 atags += ' ' * la
2025-07-01 03:03:11.890 btags += ' ' * lb
2025-07-01 03:03:11.902 else:
2025-07-01 03:03:11.912 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:11.930 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:11.942 else:
2025-07-01 03:03:11.954 # the synch pair is identical
2025-07-01 03:03:11.966 yield '  ' + aelt
2025-07-01 03:03:11.974
2025-07-01 03:03:11.986 # pump out diffs from after the synch point
2025-07-01 03:03:11.998 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:12.006
2025-07-01 03:03:12.018 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:12.038 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:12.048
2025-07-01 03:03:12.062 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:12.074 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:12.086 alo = 298, ahi = 1101
2025-07-01 03:03:12.102 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:12.110 blo = 298, bhi = 1101
2025-07-01 03:03:12.122
2025-07-01 03:03:12.130 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:12.142 g = []
2025-07-01 03:03:12.156 if alo < ahi:
2025-07-01 03:03:12.166 if blo < bhi:
2025-07-01 03:03:12.178 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:12.190 else:
2025-07-01 03:03:12.198 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:12.210 elif blo < bhi:
2025-07-01 03:03:12.218 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:12.230
2025-07-01 03:03:12.238 >       yield from g
2025-07-01 03:03:12.250
2025-07-01 03:03:12.258 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:12.270 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:12.281
2025-07-01 03:03:12.294 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:12.301 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:12.314 alo = 298, ahi = 1101
2025-07-01 03:03:12.321 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:12.334 blo = 298, bhi = 1101
2025-07-01 03:03:12.342
2025-07-01 03:03:12.354 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:12.361 r"""
2025-07-01 03:03:12.374 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:12.382 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:12.394 synch point, and intraline difference marking is done on the
2025-07-01 03:03:12.402 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:12.413
2025-07-01 03:03:12.427 Example:
2025-07-01 03:03:12.436
2025-07-01 03:03:12.459 >>> d = Differ()
2025-07-01 03:03:12.473 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:12.482 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:12.489 >>> print(''.join(results), end="")
2025-07-01 03:03:12.495 - abcDefghiJkl
2025-07-01 03:03:12.508 + abcdefGhijkl
2025-07-01 03:03:12.517 """
2025-07-01 03:03:12.522
2025-07-01 03:03:12.526 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:12.531 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:12.536 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:12.540 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:12.545 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:12.549
2025-07-01 03:03:12.554 # search for the pair that matches best without being identical
2025-07-01 03:03:12.558 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:12.563 # on junk -- unless we have to)
2025-07-01 03:03:12.567 for j in range(blo, bhi):
2025-07-01 03:03:12.572 bj = b[j]
2025-07-01 03:03:12.577 cruncher.set_seq2(bj)
2025-07-01 03:03:12.581 for i in range(alo, ahi):
2025-07-01 03:03:12.586 ai = a[i]
2025-07-01 03:03:12.590 if ai == bj:
2025-07-01 03:03:12.595 if eqi is None:
2025-07-01 03:03:12.599 eqi, eqj = i, j
2025-07-01 03:03:12.604 continue
2025-07-01 03:03:12.608 cruncher.set_seq1(ai)
2025-07-01 03:03:12.613 # computing similarity is expensive, so use the quick
2025-07-01 03:03:12.617 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:12.622 # compares by a factor of 3.
2025-07-01 03:03:12.626 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:12.631 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:12.636 # of the computation is cached by cruncher
2025-07-01 03:03:12.641 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:12.647 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:12.652 cruncher.ratio() > best_ratio:
2025-07-01 03:03:12.658 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:12.664 if best_ratio < cutoff:
2025-07-01 03:03:12.678 # no non-identical "pretty close" pair
2025-07-01 03:03:12.690 if eqi is None:
2025-07-01 03:03:12.702 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:12.718 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:12.726 return
2025-07-01 03:03:12.742 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:12.754 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:12.762 else:
2025-07-01 03:03:12.771 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:12.782 eqi = None
2025-07-01 03:03:12.799
2025-07-01 03:03:12.818 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:12.826 # identical
2025-07-01 03:03:12.838
2025-07-01 03:03:12.850 # pump out diffs from before the synch point
2025-07-01 03:03:12.862 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:12.874
2025-07-01 03:03:12.890 # do intraline marking on the synch pair
2025-07-01 03:03:12.898 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:12.906 if eqi is None:
2025-07-01 03:03:12.911 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:12.928 atags = btags = ""
2025-07-01 03:03:12.945 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:12.962 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:12.970 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:12.990 if tag == 'replace':
2025-07-01 03:03:13.002 atags += '^' * la
2025-07-01 03:03:13.013 btags += '^' * lb
2025-07-01 03:03:13.025 elif tag == 'delete':
2025-07-01 03:03:13.046 atags += '-' * la
2025-07-01 03:03:13.054 elif tag == 'insert':
2025-07-01 03:03:13.066 btags += '+' * lb
2025-07-01 03:03:13.084 elif tag == 'equal':
2025-07-01 03:03:13.094 atags += ' ' * la
2025-07-01 03:03:13.106 btags += ' ' * lb
2025-07-01 03:03:13.114 else:
2025-07-01 03:03:13.126 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:13.138 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:13.150 else:
2025-07-01 03:03:13.162 # the synch pair is identical
2025-07-01 03:03:13.174 yield '  ' + aelt
2025-07-01 03:03:13.190
2025-07-01 03:03:13.202 # pump out diffs from after the synch point
2025-07-01 03:03:13.214 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:13.224
2025-07-01 03:03:13.234 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:13.252 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:13.266
2025-07-01 03:03:13.274 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:13.286 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:13.297 alo = 299, ahi = 1101
2025-07-01 03:03:13.314 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:13.322 blo = 299, bhi = 1101
2025-07-01 03:03:13.334
2025-07-01 03:03:13.346 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:13.358 g = []
2025-07-01 03:03:13.370 if alo < ahi:
2025-07-01 03:03:13.381 if blo < bhi:
2025-07-01 03:03:13.385 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:13.399 else:
2025-07-01 03:03:13.414 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:13.422 elif blo < bhi:
2025-07-01 03:03:13.438 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:13.446
2025-07-01 03:03:13.454 >       yield from g
2025-07-01 03:03:13.466
2025-07-01 03:03:13.473 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:13.490 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:13.498
2025-07-01 03:03:13.513 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:13.529 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:13.539 alo = 299, ahi = 1101
2025-07-01 03:03:13.549 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:13.566 blo = 299, bhi = 1101
2025-07-01 03:03:13.579
2025-07-01 03:03:13.594 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:13.610 r"""
2025-07-01 03:03:13.628 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:13.638 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:13.654 synch point, and intraline difference marking is done on the
2025-07-01 03:03:13.663 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:13.670
2025-07-01 03:03:13.681 Example:
2025-07-01 03:03:13.690
2025-07-01 03:03:13.702 >>> d = Differ()
2025-07-01 03:03:13.710 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:13.723 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:13.737 >>> print(''.join(results), end="")
2025-07-01 03:03:13.750 - abcDefghiJkl
2025-07-01 03:03:13.770 + abcdefGhijkl
2025-07-01 03:03:13.794 """
2025-07-01 03:03:13.806
2025-07-01 03:03:13.818 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:13.827 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:13.841 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:13.853 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:13.858 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:13.866
2025-07-01 03:03:13.878 # search for the pair that matches best without being identical
2025-07-01 03:03:13.885 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:13.898 # on junk -- unless we have to)
2025-07-01 03:03:13.905 for j in range(blo, bhi):
2025-07-01 03:03:13.918 bj = b[j]
2025-07-01 03:03:13.925 cruncher.set_seq2(bj)
2025-07-01 03:03:13.938 for i in range(alo, ahi):
2025-07-01 03:03:13.946 ai = a[i]
2025-07-01 03:03:13.952 if ai == bj:
2025-07-01 03:03:13.966 if eqi is None:
2025-07-01 03:03:13.973 eqi, eqj = i, j
2025-07-01 03:03:13.986 continue
2025-07-01 03:03:13.994 cruncher.set_seq1(ai)
2025-07-01 03:03:14.006 # computing similarity is expensive, so use the quick
2025-07-01 03:03:14.020 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:14.030 # compares by a factor of 3.
2025-07-01 03:03:14.042 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:14.054 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:14.065 # of the computation is cached by cruncher
2025-07-01 03:03:14.082 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:14.094 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:14.110 cruncher.ratio() > best_ratio:
2025-07-01 03:03:14.118 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:14.134 if best_ratio < cutoff:
2025-07-01 03:03:14.142 # no non-identical "pretty close" pair
2025-07-01 03:03:14.154 if eqi is None:
2025-07-01 03:03:14.170 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:14.177 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:14.194 return
2025-07-01 03:03:14.203 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:14.214 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:14.222 else:
2025-07-01 03:03:14.237 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:14.246 eqi = None
2025-07-01 03:03:14.262
2025-07-01 03:03:14.272 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:14.286 # identical
2025-07-01 03:03:14.301
2025-07-01 03:03:14.310 # pump out diffs from before the synch point
2025-07-01 03:03:14.322 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:14.334
2025-07-01 03:03:14.350 # do intraline marking on the synch pair
2025-07-01 03:03:14.362 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:14.380 if eqi is None:
2025-07-01 03:03:14.390 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:14.405 atags = btags = ""
2025-07-01 03:03:14.418 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:14.425 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:14.442 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:14.449 if tag == 'replace':
2025-07-01 03:03:14.462 atags += '^' * la
2025-07-01 03:03:14.469 btags += '^' * lb
2025-07-01 03:03:14.482 elif tag == 'delete':
2025-07-01 03:03:14.498 atags += '-' * la
2025-07-01 03:03:14.510 elif tag == 'insert':
2025-07-01 03:03:14.522 btags += '+' * lb
2025-07-01 03:03:14.533 elif tag == 'equal':
2025-07-01 03:03:14.546 atags += ' ' * la
2025-07-01 03:03:14.554 btags += ' ' * lb
2025-07-01 03:03:14.566 else:
2025-07-01 03:03:14.578 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:14.585 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:14.598 else:
2025-07-01 03:03:14.614 # the synch pair is identical
2025-07-01 03:03:14.622 yield '  ' + aelt
2025-07-01 03:03:14.638
2025-07-01 03:03:14.645 # pump out diffs from after the synch point
2025-07-01 03:03:14.662 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:14.670
2025-07-01 03:03:14.686 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:14.694 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:14.709
2025-07-01 03:03:14.718 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:14.734 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:14.742 alo = 300, ahi = 1101
2025-07-01 03:03:14.758 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:14.766 blo = 300, bhi = 1101
2025-07-01 03:03:14.774
2025-07-01 03:03:14.786 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:14.794 g = []
2025-07-01 03:03:14.810 if alo < ahi:
2025-07-01 03:03:14.818 if blo < bhi:
2025-07-01 03:03:14.834 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:14.846 else:
2025-07-01 03:03:14.858 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:14.866 elif blo < bhi:
2025-07-01 03:03:14.878 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:14.885
2025-07-01 03:03:14.897 >       yield from g
2025-07-01 03:03:14.910
2025-07-01 03:03:14.923 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:14.929 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:14.942
2025-07-01 03:03:14.956 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:14.970 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:14.982 alo = 300, ahi = 1101
2025-07-01 03:03:14.993 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:15.010 blo = 300, bhi = 1101
2025-07-01 03:03:15.022
2025-07-01 03:03:15.034 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:15.042 r"""
2025-07-01 03:03:15.054 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:15.066 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:15.074 synch point, and intraline difference marking is done on the
2025-07-01 03:03:15.086 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:15.093
2025-07-01 03:03:15.108 Example:
2025-07-01 03:03:15.118
2025-07-01 03:03:15.130 >>> d = Differ()
2025-07-01 03:03:15.138 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:15.150 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:15.158 >>> print(''.join(results), end="")
2025-07-01 03:03:15.174 - abcDefghiJkl
2025-07-01 03:03:15.202 + abcdefGhijkl
2025-07-01 03:03:15.226 """
2025-07-01 03:03:15.234
2025-07-01 03:03:15.250 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:15.263 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:15.278 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:15.285 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:15.298 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:15.305
2025-07-01 03:03:15.318 # search for the pair that matches best without being identical
2025-07-01 03:03:15.330 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:15.338 # on junk -- unless we have to)
2025-07-01 03:03:15.350 for j in range(blo, bhi):
2025-07-01 03:03:15.357 bj = b[j]
2025-07-01 03:03:15.368 cruncher.set_seq2(bj)
2025-07-01 03:03:15.382 for i in range(alo, ahi):
2025-07-01 03:03:15.394 ai = a[i]
2025-07-01 03:03:15.402 if ai == bj:
2025-07-01 03:03:15.409 if eqi is None:
2025-07-01 03:03:15.424 eqi, eqj = i, j
2025-07-01 03:03:15.435 continue
2025-07-01 03:03:15.449 cruncher.set_seq1(ai)
2025-07-01 03:03:15.458 # computing similarity is expensive, so use the quick
2025-07-01 03:03:15.473 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:15.485 # compares by a factor of 3.
2025-07-01 03:03:15.498 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:15.506 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:15.516 # of the computation is cached by cruncher
2025-07-01 03:03:15.522 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:15.532 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:15.540 cruncher.ratio() > best_ratio:
2025-07-01 03:03:15.545 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:15.551 if best_ratio < cutoff:
2025-07-01 03:03:15.557 # no non-identical "pretty close" pair
2025-07-01 03:03:15.564 if eqi is None:
2025-07-01 03:03:15.574 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:15.586 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:15.610 return
2025-07-01 03:03:15.622 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:15.634 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:15.643 else:
2025-07-01 03:03:15.658 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:15.666 eqi = None
2025-07-01 03:03:15.678
2025-07-01 03:03:15.690 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:15.702 # identical
2025-07-01 03:03:15.714
2025-07-01 03:03:15.722 # pump out diffs from before the synch point
2025-07-01 03:03:15.734 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:15.746
2025-07-01 03:03:15.754 # do intraline marking on the synch pair
2025-07-01 03:03:15.768 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:15.778 if eqi is None:
2025-07-01 03:03:15.790 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:15.802 atags = btags = ""
2025-07-01 03:03:15.816 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:15.826 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:15.849 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:15.862 if tag == 'replace':
2025-07-01 03:03:15.870 atags += '^' * la
2025-07-01 03:03:15.882 btags += '^' * lb
2025-07-01 03:03:15.894 elif tag == 'delete':
2025-07-01 03:03:15.902 atags += '-' * la
2025-07-01 03:03:15.910 elif tag == 'insert':
2025-07-01 03:03:15.918 btags += '+' * lb
2025-07-01 03:03:15.930 elif tag == 'equal':
2025-07-01 03:03:15.942 atags += ' ' * la
2025-07-01 03:03:15.954 btags += ' ' * lb
2025-07-01 03:03:15.962 else:
2025-07-01 03:03:15.974 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:15.986 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:15.996 else:
2025-07-01 03:03:16.010 # the synch pair is identical
2025-07-01 03:03:16.022 yield '  ' + aelt
2025-07-01 03:03:16.034
2025-07-01 03:03:16.040 # pump out diffs from after the synch point
2025-07-01 03:03:16.054 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:16.066
2025-07-01 03:03:16.077 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:16.088 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:16.106
2025-07-01 03:03:16.117 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:16.133 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:16.145 alo = 301, ahi = 1101
2025-07-01 03:03:16.165 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:16.175 blo = 301, bhi = 1101
2025-07-01 03:03:16.181
2025-07-01 03:03:16.186 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:16.194 g = []
2025-07-01 03:03:16.200 if alo < ahi:
2025-07-01 03:03:16.205 if blo < bhi:
2025-07-01 03:03:16.209 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:16.214 else:
2025-07-01 03:03:16.218 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:16.223 elif blo < bhi:
2025-07-01 03:03:16.228 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:16.233
2025-07-01 03:03:16.237 >       yield from g
2025-07-01 03:03:16.241
2025-07-01 03:03:16.246 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:16.250 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:16.255
2025-07-01 03:03:16.261 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:16.266 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:16.270 alo = 301, ahi = 1101
2025-07-01 03:03:16.276 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:16.280 blo = 301, bhi = 1101
2025-07-01 03:03:16.285
2025-07-01 03:03:16.289 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:16.294 r"""
2025-07-01 03:03:16.298 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:16.303 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:16.308 synch point, and intraline difference marking is done on the
2025-07-01 03:03:16.313 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:16.317
2025-07-01 03:03:16.321 Example:
2025-07-01 03:03:16.337
2025-07-01 03:03:16.348 >>> d = Differ()
2025-07-01 03:03:16.362 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:16.374 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:16.386 >>> print(''.join(results), end="")
2025-07-01 03:03:16.394 - abcDefghiJkl
2025-07-01 03:03:16.419 + abcdefGhijkl
2025-07-01 03:03:16.450 """
2025-07-01 03:03:16.462
2025-07-01 03:03:16.478 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:16.487 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:16.498 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:16.522 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:16.530 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:16.542
2025-07-01 03:03:16.558 # search for the pair that matches best without being identical
2025-07-01 03:03:16.578 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:16.589 # on junk -- unless we have to)
2025-07-01 03:03:16.602 for j in range(blo, bhi):
2025-07-01 03:03:16.614 bj = b[j]
2025-07-01 03:03:16.624 cruncher.set_seq2(bj)
2025-07-01 03:03:16.634 for i in range(alo, ahi):
2025-07-01 03:03:16.654 ai = a[i]
2025-07-01 03:03:16.669 if ai == bj:
2025-07-01 03:03:16.676 if eqi is None:
2025-07-01 03:03:16.693 eqi, eqj = i, j
2025-07-01 03:03:16.706 continue
2025-07-01 03:03:16.711 cruncher.set_seq1(ai)
2025-07-01 03:03:16.716 # computing similarity is expensive, so use the quick
2025-07-01 03:03:16.734 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:16.746 # compares by a factor of 3.
2025-07-01 03:03:16.754 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:16.766 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:16.771 # of the computation is cached by cruncher
2025-07-01 03:03:16.790 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:16.802 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:16.814 cruncher.ratio() > best_ratio:
2025-07-01 03:03:16.828 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:16.838 if best_ratio < cutoff:
2025-07-01 03:03:16.850 # no non-identical "pretty close" pair
2025-07-01 03:03:16.859 if eqi is None:
2025-07-01 03:03:16.874 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:16.882 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:16.894 return
2025-07-01 03:03:16.902 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:16.918 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:16.926 else:
2025-07-01 03:03:16.937 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:16.946 eqi = None
2025-07-01 03:03:16.957
2025-07-01 03:03:16.966 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:16.974 # identical
2025-07-01 03:03:16.990
2025-07-01 03:03:16.999 # pump out diffs from before the synch point
2025-07-01 03:03:17.010 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:17.026
2025-07-01 03:03:17.038 # do intraline marking on the synch pair
2025-07-01 03:03:17.044 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:17.062 if eqi is None:
2025-07-01 03:03:17.074 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:17.086 atags = btags = ""
2025-07-01 03:03:17.094 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:17.106 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:17.112 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:17.126 if tag == 'replace':
2025-07-01 03:03:17.142 atags += '^' * la
2025-07-01 03:03:17.151 btags += '^' * lb
2025-07-01 03:03:17.156 elif tag == 'delete':
2025-07-01 03:03:17.160 atags += '-' * la
2025-07-01 03:03:17.166 elif tag == 'insert':
2025-07-01 03:03:17.171 btags += '+' * lb
2025-07-01 03:03:17.176 elif tag == 'equal':
2025-07-01 03:03:17.181 atags += ' ' * la
2025-07-01 03:03:17.186 btags += ' ' * lb
2025-07-01 03:03:17.191 else:
2025-07-01 03:03:17.196 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:17.201 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:17.206 else:
2025-07-01 03:03:17.212 # the synch pair is identical
2025-07-01 03:03:17.217 yield '  ' + aelt
2025-07-01 03:03:17.222
2025-07-01 03:03:17.227 # pump out diffs from after the synch point
2025-07-01 03:03:17.233 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:17.237
2025-07-01 03:03:17.242 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:17.247 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:17.254
2025-07-01 03:03:17.258 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:17.263 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:17.268 alo = 302, ahi = 1101
2025-07-01 03:03:17.273 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:17.278 blo = 302, bhi = 1101
2025-07-01 03:03:17.282
2025-07-01 03:03:17.287 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:17.291 g = []
2025-07-01 03:03:17.296 if alo < ahi:
2025-07-01 03:03:17.300 if blo < bhi:
2025-07-01 03:03:17.305 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:17.309 else:
2025-07-01 03:03:17.314 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:17.318 elif blo < bhi:
2025-07-01 03:03:17.323 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:17.327
2025-07-01 03:03:17.331 >       yield from g
2025-07-01 03:03:17.336
2025-07-01 03:03:17.340 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:17.345 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:17.349
2025-07-01 03:03:17.354 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:17.359 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:17.364 alo = 302, ahi = 1101
2025-07-01 03:03:17.370 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:17.375 blo = 302, bhi = 1101
2025-07-01 03:03:17.381
2025-07-01 03:03:17.387 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:17.392 r"""
2025-07-01 03:03:17.397 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:17.402 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:17.407 synch point, and intraline difference marking is done on the
2025-07-01 03:03:17.413 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:17.419
2025-07-01 03:03:17.424 Example:
2025-07-01 03:03:17.430
2025-07-01 03:03:17.435 >>> d = Differ()
2025-07-01 03:03:17.441 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:17.446 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:17.451 >>> print(''.join(results), end="")
2025-07-01 03:03:17.455 - abcDefghiJkl
2025-07-01 03:03:17.465 + abcdefGhijkl
2025-07-01 03:03:17.474 """
2025-07-01 03:03:17.479
2025-07-01 03:03:17.484 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:17.488 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:17.493 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:17.498 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:17.502 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:17.507
2025-07-01 03:03:17.511 # search for the pair that matches best without being identical
2025-07-01 03:03:17.516 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:17.521 # on junk -- unless we have to)
2025-07-01 03:03:17.526 for j in range(blo, bhi):
2025-07-01 03:03:17.531 bj = b[j]
2025-07-01 03:03:17.536 cruncher.set_seq2(bj)
2025-07-01 03:03:17.541 for i in range(alo, ahi):
2025-07-01 03:03:17.546 ai = a[i]
2025-07-01 03:03:17.552 if ai == bj:
2025-07-01 03:03:17.557 if eqi is None:
2025-07-01 03:03:17.562 eqi, eqj = i, j
2025-07-01 03:03:17.567 continue
2025-07-01 03:03:17.577 cruncher.set_seq1(ai)
2025-07-01 03:03:17.586 # computing similarity is expensive, so use the quick
2025-07-01 03:03:17.593 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:17.605 # compares by a factor of 3.
2025-07-01 03:03:17.618 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:17.634 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:17.642 # of the computation is cached by cruncher
2025-07-01 03:03:17.654 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:17.666 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:17.677 cruncher.ratio() > best_ratio:
2025-07-01 03:03:17.694 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:17.706 if best_ratio < cutoff:
2025-07-01 03:03:17.718 # no non-identical "pretty close" pair
2025-07-01 03:03:17.728 if eqi is None:
2025-07-01 03:03:17.741 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:17.753 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:17.757 return
2025-07-01 03:03:17.762 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:17.766 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:17.771 else:
2025-07-01 03:03:17.775 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:17.780 eqi = None
2025-07-01 03:03:17.784
2025-07-01 03:03:17.789 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:17.793 # identical
2025-07-01 03:03:17.798
2025-07-01 03:03:17.802 # pump out diffs from before the synch point
2025-07-01 03:03:17.807 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:17.811
2025-07-01 03:03:17.815 # do intraline marking on the synch pair
2025-07-01 03:03:17.820 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:17.824 if eqi is None:
2025-07-01 03:03:17.829 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:17.833 atags = btags = ""
2025-07-01 03:03:17.838 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:17.842 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:17.847 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:17.851 if tag == 'replace':
2025-07-01 03:03:17.856 atags += '^' * la
2025-07-01 03:03:17.860 btags += '^' * lb
2025-07-01 03:03:17.865 elif tag == 'delete':
2025-07-01 03:03:17.869 atags += '-' * la
2025-07-01 03:03:17.873 elif tag == 'insert':
2025-07-01 03:03:17.878 btags += '+' * lb
2025-07-01 03:03:17.882 elif tag == 'equal':
2025-07-01 03:03:17.887 atags += ' ' * la
2025-07-01 03:03:17.892 btags += ' ' * lb
2025-07-01 03:03:17.896 else:
2025-07-01 03:03:17.901 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:17.905 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:17.910 else:
2025-07-01 03:03:17.915 # the synch pair is identical
2025-07-01 03:03:17.919 yield '  ' + aelt
2025-07-01 03:03:17.923
2025-07-01 03:03:17.928 # pump out diffs from after the synch point
2025-07-01 03:03:17.933 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:17.937
2025-07-01 03:03:17.942 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:17.946 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:17.951
2025-07-01 03:03:17.955 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:17.961 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:17.965 alo = 303, ahi = 1101
2025-07-01 03:03:17.970 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:17.974 blo = 303, bhi = 1101
2025-07-01 03:03:17.979
2025-07-01 03:03:17.983 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:17.987 g = []
2025-07-01 03:03:17.992 if alo < ahi:
2025-07-01 03:03:17.996 if blo < bhi:
2025-07-01 03:03:18.000 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:18.005 else:
2025-07-01 03:03:18.009 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:18.014 elif blo < bhi:
2025-07-01 03:03:18.018 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:18.022
2025-07-01 03:03:18.026 >       yield from g
2025-07-01 03:03:18.031
2025-07-01 03:03:18.035 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:18.039 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:18.044
2025-07-01 03:03:18.048 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:18.053 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:18.057 alo = 303, ahi = 1101
2025-07-01 03:03:18.062 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:18.066 blo = 303, bhi = 1101
2025-07-01 03:03:18.070
2025-07-01 03:03:18.075 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:18.079 r"""
2025-07-01 03:03:18.083 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:18.088 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:18.092 synch point, and intraline difference marking is done on the
2025-07-01 03:03:18.097 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:18.101
2025-07-01 03:03:18.105 Example:
2025-07-01 03:03:18.110
2025-07-01 03:03:18.114 >>> d = Differ()
2025-07-01 03:03:18.119 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:18.123 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:18.128 >>> print(''.join(results), end="")
2025-07-01 03:03:18.132 - abcDefghiJkl
2025-07-01 03:03:18.141 + abcdefGhijkl
2025-07-01 03:03:18.149 """
2025-07-01 03:03:18.154
2025-07-01 03:03:18.159 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:18.163 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:18.167 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:18.172 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:18.176 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:18.181
2025-07-01 03:03:18.185 # search for the pair that matches best without being identical
2025-07-01 03:03:18.190 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:18.194 # on junk -- unless we have to)
2025-07-01 03:03:18.199 for j in range(blo, bhi):
2025-07-01 03:03:18.203 bj = b[j]
2025-07-01 03:03:18.208 cruncher.set_seq2(bj)
2025-07-01 03:03:18.212 for i in range(alo, ahi):
2025-07-01 03:03:18.217 ai = a[i]
2025-07-01 03:03:18.221 if ai == bj:
2025-07-01 03:03:18.226 if eqi is None:
2025-07-01 03:03:18.231 eqi, eqj = i, j
2025-07-01 03:03:18.236 continue
2025-07-01 03:03:18.241 cruncher.set_seq1(ai)
2025-07-01 03:03:18.247 # computing similarity is expensive, so use the quick
2025-07-01 03:03:18.253 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:18.258 # compares by a factor of 3.
2025-07-01 03:03:18.263 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:18.268 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:18.274 # of the computation is cached by cruncher
2025-07-01 03:03:18.279 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:18.285 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:18.290 cruncher.ratio() > best_ratio:
2025-07-01 03:03:18.295 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:18.301 if best_ratio < cutoff:
2025-07-01 03:03:18.306 # no non-identical "pretty close" pair
2025-07-01 03:03:18.312 if eqi is None:
2025-07-01 03:03:18.316 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:18.321 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:18.325 return
2025-07-01 03:03:18.330 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:18.334 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:18.339 else:
2025-07-01 03:03:18.344 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:18.349 eqi = None
2025-07-01 03:03:18.354
2025-07-01 03:03:18.359 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:18.364 # identical
2025-07-01 03:03:18.369
2025-07-01 03:03:18.374 # pump out diffs from before the synch point
2025-07-01 03:03:18.379 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:18.383
2025-07-01 03:03:18.388 # do intraline marking on the synch pair
2025-07-01 03:03:18.393 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:18.397 if eqi is None:
2025-07-01 03:03:18.402 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:18.406 atags = btags = ""
2025-07-01 03:03:18.411 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:18.416 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:18.421 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:18.425 if tag == 'replace':
2025-07-01 03:03:18.430 atags += '^' * la
2025-07-01 03:03:18.436 btags += '^' * lb
2025-07-01 03:03:18.441 elif tag == 'delete':
2025-07-01 03:03:18.447 atags += '-' * la
2025-07-01 03:03:18.452 elif tag == 'insert':
2025-07-01 03:03:18.458 btags += '+' * lb
2025-07-01 03:03:18.463 elif tag == 'equal':
2025-07-01 03:03:18.469 atags += ' ' * la
2025-07-01 03:03:18.473 btags += ' ' * lb
2025-07-01 03:03:18.477 else:
2025-07-01 03:03:18.482 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:18.486 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:18.490 else:
2025-07-01 03:03:18.495 # the synch pair is identical
2025-07-01 03:03:18.499 yield '  ' + aelt
2025-07-01 03:03:18.504
2025-07-01 03:03:18.509 # pump out diffs from after the synch point
2025-07-01 03:03:18.514 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:18.519
2025-07-01 03:03:18.524 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:18.529 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:18.533
2025-07-01 03:03:18.537 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:18.542 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:18.547 alo = 304, ahi = 1101
2025-07-01 03:03:18.551 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:18.556 blo = 304, bhi = 1101
2025-07-01 03:03:18.560
2025-07-01 03:03:18.564 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:18.569 g = []
2025-07-01 03:03:18.573 if alo < ahi:
2025-07-01 03:03:18.578 if blo < bhi:
2025-07-01 03:03:18.583 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:18.588 else:
2025-07-01 03:03:18.594 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:18.599 elif blo < bhi:
2025-07-01 03:03:18.604 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:18.609
2025-07-01 03:03:18.613 >       yield from g
2025-07-01 03:03:18.618
2025-07-01 03:03:18.622 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:18.627 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:18.631
2025-07-01 03:03:18.636 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:18.641 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:18.646 alo = 304, ahi = 1101
2025-07-01 03:03:18.652 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:18.658 blo = 304, bhi = 1101
2025-07-01 03:03:18.664
2025-07-01 03:03:18.669 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:18.675 r"""
2025-07-01 03:03:18.681 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:18.688 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:18.693 synch point, and intraline difference marking is done on the
2025-07-01 03:03:18.699 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:18.704
2025-07-01 03:03:18.709 Example:
2025-07-01 03:03:18.713
2025-07-01 03:03:18.718 >>> d = Differ()
2025-07-01 03:03:18.722 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:18.727 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:18.732 >>> print(''.join(results), end="")
2025-07-01 03:03:18.736 - abcDefghiJkl
2025-07-01 03:03:18.745 + abcdefGhijkl
2025-07-01 03:03:18.754 """
2025-07-01 03:03:18.758
2025-07-01 03:03:18.763 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:18.768 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:18.772 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:18.777 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:18.782 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:18.786
2025-07-01 03:03:18.791 # search for the pair that matches best without being identical
2025-07-01 03:03:18.795 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:18.800 # on junk -- unless we have to)
2025-07-01 03:03:18.804 for j in range(blo, bhi):
2025-07-01 03:03:18.809 bj = b[j]
2025-07-01 03:03:18.813 cruncher.set_seq2(bj)
2025-07-01 03:03:18.818 for i in range(alo, ahi):
2025-07-01 03:03:18.822 ai = a[i]
2025-07-01 03:03:18.827 if ai == bj:
2025-07-01 03:03:18.831 if eqi is None:
2025-07-01 03:03:18.836 eqi, eqj = i, j
2025-07-01 03:03:18.841 continue
2025-07-01 03:03:18.846 cruncher.set_seq1(ai)
2025-07-01 03:03:18.850 # computing similarity is expensive, so use the quick
2025-07-01 03:03:18.855 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:18.860 # compares by a factor of 3.
2025-07-01 03:03:18.865 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:18.869 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:18.874 # of the computation is cached by cruncher
2025-07-01 03:03:18.878 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:18.882 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:18.887 cruncher.ratio() > best_ratio:
2025-07-01 03:03:18.892 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:18.896 if best_ratio < cutoff:
2025-07-01 03:03:18.901 # no non-identical "pretty close" pair
2025-07-01 03:03:18.906 if eqi is None:
2025-07-01 03:03:18.913 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:18.919 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:18.925 return
2025-07-01 03:03:18.931 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:18.937 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:18.943 else:
2025-07-01 03:03:18.949 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:18.956 eqi = None
2025-07-01 03:03:18.962
2025-07-01 03:03:18.968 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:18.974 # identical
2025-07-01 03:03:18.984
2025-07-01 03:03:18.993 # pump out diffs from before the synch point
2025-07-01 03:03:19.000 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:19.006
2025-07-01 03:03:19.011 # do intraline marking on the synch pair
2025-07-01 03:03:19.016 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:19.020 if eqi is None:
2025-07-01 03:03:19.025 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:19.030 atags = btags = ""
2025-07-01 03:03:19.034 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:19.039 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:19.045 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:19.050 if tag == 'replace':
2025-07-01 03:03:19.055 atags += '^' * la
2025-07-01 03:03:19.060 btags += '^' * lb
2025-07-01 03:03:19.064 elif tag == 'delete':
2025-07-01 03:03:19.069 atags += '-' * la
2025-07-01 03:03:19.074 elif tag == 'insert':
2025-07-01 03:03:19.079 btags += '+' * lb
2025-07-01 03:03:19.084 elif tag == 'equal':
2025-07-01 03:03:19.088 atags += ' ' * la
2025-07-01 03:03:19.093 btags += ' ' * lb
2025-07-01 03:03:19.097 else:
2025-07-01 03:03:19.102 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:19.107 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:19.112 else:
2025-07-01 03:03:19.117 # the synch pair is identical
2025-07-01 03:03:19.121 yield '  ' + aelt
2025-07-01 03:03:19.125
2025-07-01 03:03:19.130 # pump out diffs from after the synch point
2025-07-01 03:03:19.134 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:19.139
2025-07-01 03:03:19.143 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:19.148 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:19.152
2025-07-01 03:03:19.157 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:19.162 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:19.166 alo = 305, ahi = 1101
2025-07-01 03:03:19.171 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:19.177 blo = 305, bhi = 1101
2025-07-01 03:03:19.182
2025-07-01 03:03:19.186 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:19.190 g = []
2025-07-01 03:03:19.195 if alo < ahi:
2025-07-01 03:03:19.199 if blo < bhi:
2025-07-01 03:03:19.204 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:19.208 else:
2025-07-01 03:03:19.213 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:19.219 elif blo < bhi:
2025-07-01 03:03:19.224 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:19.228
2025-07-01 03:03:19.232 >       yield from g
2025-07-01 03:03:19.237
2025-07-01 03:03:19.241 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:19.246 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:19.250
2025-07-01 03:03:19.255 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:19.260 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:19.264 alo = 305, ahi = 1101
2025-07-01 03:03:19.269 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:19.274 blo = 305, bhi = 1101
2025-07-01 03:03:19.278
2025-07-01 03:03:19.282 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:19.287 r"""
2025-07-01 03:03:19.291 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:19.296 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:19.301 synch point, and intraline difference marking is done on the
2025-07-01 03:03:19.305 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:19.310
2025-07-01 03:03:19.314 Example:
2025-07-01 03:03:19.318
2025-07-01 03:03:19.323 >>> d = Differ()
2025-07-01 03:03:19.328 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:19.332 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:19.336 >>> print(''.join(results), end="")
2025-07-01 03:03:19.341 - abcDefghiJkl
2025-07-01 03:03:19.349 + abcdefGhijkl
2025-07-01 03:03:19.358 """
2025-07-01 03:03:19.362
2025-07-01 03:03:19.367 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:19.372 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:19.377 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:19.381 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:19.386 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:19.390
2025-07-01 03:03:19.395 # search for the pair that matches best without being identical
2025-07-01 03:03:19.400 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:19.405 # on junk -- unless we have to)
2025-07-01 03:03:19.409 for j in range(blo, bhi):
2025-07-01 03:03:19.413 bj = b[j]
2025-07-01 03:03:19.417 cruncher.set_seq2(bj)
2025-07-01 03:03:19.422 for i in range(alo, ahi):
2025-07-01 03:03:19.426 ai = a[i]
2025-07-01 03:03:19.430 if ai == bj:
2025-07-01 03:03:19.435 if eqi is None:
2025-07-01 03:03:19.439 eqi, eqj = i, j
2025-07-01 03:03:19.443 continue
2025-07-01 03:03:19.447 cruncher.set_seq1(ai)
2025-07-01 03:03:19.452 # computing similarity is expensive, so use the quick
2025-07-01 03:03:19.456 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:19.461 # compares by a factor of 3.
2025-07-01 03:03:19.465 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:19.469 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:19.474 # of the computation is cached by cruncher
2025-07-01 03:03:19.478 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:19.482 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:19.487 cruncher.ratio() > best_ratio:
2025-07-01 03:03:19.491 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:19.497 if best_ratio < cutoff:
2025-07-01 03:03:19.502 # no non-identical "pretty close" pair
2025-07-01 03:03:19.506 if eqi is None:
2025-07-01 03:03:19.511 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:19.515 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:19.520 return
2025-07-01 03:03:19.524 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:19.528 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:19.533 else:
2025-07-01 03:03:19.537 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:19.542 eqi = None
2025-07-01 03:03:19.547
2025-07-01 03:03:19.554 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:19.560 # identical
2025-07-01 03:03:19.566
2025-07-01 03:03:19.574 # pump out diffs from before the synch point
2025-07-01 03:03:19.580 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:19.586
2025-07-01 03:03:19.592 # do intraline marking on the synch pair
2025-07-01 03:03:19.597 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:19.602 if eqi is None:
2025-07-01 03:03:19.606 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:19.611 atags = btags = ""
2025-07-01 03:03:19.615 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:19.620 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:19.624 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:19.629 if tag == 'replace':
2025-07-01 03:03:19.633 atags += '^' * la
2025-07-01 03:03:19.638 btags += '^' * lb
2025-07-01 03:03:19.642 elif tag == 'delete':
2025-07-01 03:03:19.646 atags += '-' * la
2025-07-01 03:03:19.651 elif tag == 'insert':
2025-07-01 03:03:19.655 btags += '+' * lb
2025-07-01 03:03:19.659 elif tag == 'equal':
2025-07-01 03:03:19.664 atags += ' ' * la
2025-07-01 03:03:19.668 btags += ' ' * lb
2025-07-01 03:03:19.672 else:
2025-07-01 03:03:19.677 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:19.682 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:19.688 else:
2025-07-01 03:03:19.694 # the synch pair is identical
2025-07-01 03:03:19.700 yield '  ' + aelt
2025-07-01 03:03:19.705
2025-07-01 03:03:19.711 # pump out diffs from after the synch point
2025-07-01 03:03:19.717 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:19.723
2025-07-01 03:03:19.729 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:19.735 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:19.741
2025-07-01 03:03:19.747 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:19.753 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:19.758 alo = 306, ahi = 1101
2025-07-01 03:03:19.763 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:19.767 blo = 306, bhi = 1101
2025-07-01 03:03:19.771
2025-07-01 03:03:19.775 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:19.780 g = []
2025-07-01 03:03:19.784 if alo < ahi:
2025-07-01 03:03:19.788 if blo < bhi:
2025-07-01 03:03:19.793 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:19.797 else:
2025-07-01 03:03:19.801 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:19.806 elif blo < bhi:
2025-07-01 03:03:19.811 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:19.815
2025-07-01 03:03:19.819 >       yield from g
2025-07-01 03:03:19.824
2025-07-01 03:03:19.828 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:19.832 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:19.836
2025-07-01 03:03:19.841 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:19.845 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:19.849 alo = 306, ahi = 1101
2025-07-01 03:03:19.854 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:19.858 blo = 306, bhi = 1101
2025-07-01 03:03:19.862
2025-07-01 03:03:19.867 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:19.871 r"""
2025-07-01 03:03:19.875 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:19.880 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:19.884 synch point, and intraline difference marking is done on the
2025-07-01 03:03:19.888 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:19.892
2025-07-01 03:03:19.897 Example:
2025-07-01 03:03:19.901
2025-07-01 03:03:19.905 >>> d = Differ()
2025-07-01 03:03:19.909 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:19.914 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:19.918 >>> print(''.join(results), end="")
2025-07-01 03:03:19.922 - abcDefghiJkl
2025-07-01 03:03:19.932 + abcdefGhijkl
2025-07-01 03:03:19.941 """
2025-07-01 03:03:19.945
2025-07-01 03:03:19.950 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:19.954 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:19.958 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:19.963 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:19.967 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:19.971
2025-07-01 03:03:19.976 # search for the pair that matches best without being identical
2025-07-01 03:03:19.980 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:19.984 # on junk -- unless we have to)
2025-07-01 03:03:19.989 for j in range(blo, bhi):
2025-07-01 03:03:19.993 bj = b[j]
2025-07-01 03:03:19.997 cruncher.set_seq2(bj)
2025-07-01 03:03:20.002 for i in range(alo, ahi):
2025-07-01 03:03:20.006 ai = a[i]
2025-07-01 03:03:20.010 if ai == bj:
2025-07-01 03:03:20.015 if eqi is None:
2025-07-01 03:03:20.020 eqi, eqj = i, j
2025-07-01 03:03:20.024 continue
2025-07-01 03:03:20.029 cruncher.set_seq1(ai)
2025-07-01 03:03:20.034 # computing similarity is expensive, so use the quick
2025-07-01 03:03:20.039 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:20.043 # compares by a factor of 3.
2025-07-01 03:03:20.048 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:20.053 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:20.057 # of the computation is cached by cruncher
2025-07-01 03:03:20.062 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:20.066 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:20.070 cruncher.ratio() > best_ratio:
2025-07-01 03:03:20.075 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:20.079 if best_ratio < cutoff:
2025-07-01 03:03:20.083 # no non-identical "pretty close" pair
2025-07-01 03:03:20.088 if eqi is None:
2025-07-01 03:03:20.092 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:20.096 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:20.101 return
2025-07-01 03:03:20.105 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:20.110 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:20.114 else:
2025-07-01 03:03:20.119 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:20.123 eqi = None
2025-07-01 03:03:20.127
2025-07-01 03:03:20.132 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:20.136 # identical
2025-07-01 03:03:20.140
2025-07-01 03:03:20.145 # pump out diffs from before the synch point
2025-07-01 03:03:20.149 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:20.153
2025-07-01 03:03:20.158 # do intraline marking on the synch pair
2025-07-01 03:03:20.162 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:20.167 if eqi is None:
2025-07-01 03:03:20.171 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:20.175 atags = btags = ""
2025-07-01 03:03:20.180 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:20.186 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:20.195 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:20.206 if tag == 'replace':
2025-07-01 03:03:20.213 atags += '^' * la
2025-07-01 03:03:20.218 btags += '^' * lb
2025-07-01 03:03:20.222 elif tag == 'delete':
2025-07-01 03:03:20.227 atags += '-' * la
2025-07-01 03:03:20.231 elif tag == 'insert':
2025-07-01 03:03:20.236 btags += '+' * lb
2025-07-01 03:03:20.241 elif tag == 'equal':
2025-07-01 03:03:20.245 atags += ' ' * la
2025-07-01 03:03:20.249 btags += ' ' * lb
2025-07-01 03:03:20.254 else:
2025-07-01 03:03:20.258 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:20.263 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:20.268 else:
2025-07-01 03:03:20.272 # the synch pair is identical
2025-07-01 03:03:20.277 yield '  ' + aelt
2025-07-01 03:03:20.282
2025-07-01 03:03:20.287 # pump out diffs from after the synch point
2025-07-01 03:03:20.292 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:20.296
2025-07-01 03:03:20.301 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:20.305 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:20.310
2025-07-01 03:03:20.314 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:20.320 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:20.324 alo = 307, ahi = 1101
2025-07-01 03:03:20.329 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:20.333 blo = 307, bhi = 1101
2025-07-01 03:03:20.337
2025-07-01 03:03:20.342 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:20.346 g = []
2025-07-01 03:03:20.350 if alo < ahi:
2025-07-01 03:03:20.355 if blo < bhi:
2025-07-01 03:03:20.359 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:20.364 else:
2025-07-01 03:03:20.368 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:20.373 elif blo < bhi:
2025-07-01 03:03:20.378 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:20.382
2025-07-01 03:03:20.386 >       yield from g
2025-07-01 03:03:20.390
2025-07-01 03:03:20.395 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:20.399 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:20.403
2025-07-01 03:03:20.408 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:20.412 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:20.417 alo = 307, ahi = 1101
2025-07-01 03:03:20.422 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:20.426 blo = 307, bhi = 1101
2025-07-01 03:03:20.430
2025-07-01 03:03:20.435 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:20.439 r"""
2025-07-01 03:03:20.443 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:20.447 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:20.452 synch point, and intraline difference marking is done on the
2025-07-01 03:03:20.456 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:20.461
2025-07-01 03:03:20.465 Example:
2025-07-01 03:03:20.470
2025-07-01 03:03:20.474 >>> d = Differ()
2025-07-01 03:03:20.479 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:20.483 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:20.487 >>> print(''.join(results), end="")
2025-07-01 03:03:20.492 - abcDefghiJkl
2025-07-01 03:03:20.500 + abcdefGhijkl
2025-07-01 03:03:20.509 """
2025-07-01 03:03:20.513
2025-07-01 03:03:20.517 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:20.522 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:20.526 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:20.531 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:20.535 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:20.539
2025-07-01 03:03:20.544 # search for the pair that matches best without being identical
2025-07-01 03:03:20.548 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:20.553 # on junk -- unless we have to)
2025-07-01 03:03:20.557 for j in range(blo, bhi):
2025-07-01 03:03:20.561 bj = b[j]
2025-07-01 03:03:20.566 cruncher.set_seq2(bj)
2025-07-01 03:03:20.570 for i in range(alo, ahi):
2025-07-01 03:03:20.574 ai = a[i]
2025-07-01 03:03:20.579 if ai == bj:
2025-07-01 03:03:20.583 if eqi is None:
2025-07-01 03:03:20.588 eqi, eqj = i, j
2025-07-01 03:03:20.592 continue
2025-07-01 03:03:20.596 cruncher.set_seq1(ai)
2025-07-01 03:03:20.601 # computing similarity is expensive, so use the quick
2025-07-01 03:03:20.605 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:20.610 # compares by a factor of 3.
2025-07-01 03:03:20.614 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:20.619 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:20.623 # of the computation is cached by cruncher
2025-07-01 03:03:20.628 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:20.632 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:20.636 cruncher.ratio() > best_ratio:
2025-07-01 03:03:20.641 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:20.645 if best_ratio < cutoff:
2025-07-01 03:03:20.649 # no non-identical "pretty close" pair
2025-07-01 03:03:20.654 if eqi is None:
2025-07-01 03:03:20.658 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:20.663 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:20.667 return
2025-07-01 03:03:20.672 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:20.676 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:20.681 else:
2025-07-01 03:03:20.685 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:20.690 eqi = None
2025-07-01 03:03:20.694
2025-07-01 03:03:20.699 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:20.703 # identical
2025-07-01 03:03:20.707
2025-07-01 03:03:20.712 # pump out diffs from before the synch point
2025-07-01 03:03:20.716 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:20.721
2025-07-01 03:03:20.725 # do intraline marking on the synch pair
2025-07-01 03:03:20.730 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:20.734 if eqi is None:
2025-07-01 03:03:20.739 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:20.743 atags = btags = ""
2025-07-01 03:03:20.747 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:20.752 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:20.756 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:20.761 if tag == 'replace':
2025-07-01 03:03:20.765 atags += '^' * la
2025-07-01 03:03:20.770 btags += '^' * lb
2025-07-01 03:03:20.774 elif tag == 'delete':
2025-07-01 03:03:20.779 atags += '-' * la
2025-07-01 03:03:20.783 elif tag == 'insert':
2025-07-01 03:03:20.788 btags += '+' * lb
2025-07-01 03:03:20.792 elif tag == 'equal':
2025-07-01 03:03:20.797 atags += ' ' * la
2025-07-01 03:03:20.801 btags += ' ' * lb
2025-07-01 03:03:20.806 else:
2025-07-01 03:03:20.810 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:20.815 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:20.819 else:
2025-07-01 03:03:20.824 # the synch pair is identical
2025-07-01 03:03:20.828 yield '  ' + aelt
2025-07-01 03:03:20.833
2025-07-01 03:03:20.837 # pump out diffs from after the synch point
2025-07-01 03:03:20.841 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:20.846
2025-07-01 03:03:20.850 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:20.855 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:20.859
2025-07-01 03:03:20.863 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:20.871 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:20.878 alo = 308, ahi = 1101
2025-07-01 03:03:20.882 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:20.887 blo = 308, bhi = 1101
2025-07-01 03:03:20.891
2025-07-01 03:03:20.896 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:20.900 g = []
2025-07-01 03:03:20.905 if alo < ahi:
2025-07-01 03:03:20.910 if blo < bhi:
2025-07-01 03:03:20.914 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:20.919 else:
2025-07-01 03:03:20.924 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:20.928 elif blo < bhi:
2025-07-01 03:03:20.932 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:20.937
2025-07-01 03:03:20.941 >       yield from g
2025-07-01 03:03:20.945
2025-07-01 03:03:20.950 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:20.954 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:20.958
2025-07-01 03:03:20.963 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:20.968 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:20.973 alo = 308, ahi = 1101
2025-07-01 03:03:20.977 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:20.982 blo = 308, bhi = 1101
2025-07-01 03:03:20.986
2025-07-01 03:03:20.990 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:20.994 r"""
2025-07-01 03:03:20.999 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:21.004 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:21.009 synch point, and intraline difference marking is done on the
2025-07-01 03:03:21.013 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:21.018
2025-07-01 03:03:21.022 Example:
2025-07-01 03:03:21.026
2025-07-01 03:03:21.031 >>> d = Differ()
2025-07-01 03:03:21.036 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:21.040 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:21.045 >>> print(''.join(results), end="")
2025-07-01 03:03:21.049 - abcDefghiJkl
2025-07-01 03:03:21.058 + abcdefGhijkl
2025-07-01 03:03:21.067 """
2025-07-01 03:03:21.072
2025-07-01 03:03:21.076 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:21.081 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:21.085 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:21.090 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:21.094 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:21.099
2025-07-01 03:03:21.103 # search for the pair that matches best without being identical
2025-07-01 03:03:21.108 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:21.112 # on junk -- unless we have to)
2025-07-01 03:03:21.117 for j in range(blo, bhi):
2025-07-01 03:03:21.121 bj = b[j]
2025-07-01 03:03:21.125 cruncher.set_seq2(bj)
2025-07-01 03:03:21.130 for i in range(alo, ahi):
2025-07-01 03:03:21.134 ai = a[i]
2025-07-01 03:03:21.139 if ai == bj:
2025-07-01 03:03:21.143 if eqi is None:
2025-07-01 03:03:21.148 eqi, eqj = i, j
2025-07-01 03:03:21.152 continue
2025-07-01 03:03:21.157 cruncher.set_seq1(ai)
2025-07-01 03:03:21.161 # computing similarity is expensive, so use the quick
2025-07-01 03:03:21.166 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:21.171 # compares by a factor of 3.
2025-07-01 03:03:21.176 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:21.181 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:21.185 # of the computation is cached by cruncher
2025-07-01 03:03:21.190 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:21.195 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:21.199 cruncher.ratio() > best_ratio:
2025-07-01 03:03:21.204 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:21.209 if best_ratio < cutoff:
2025-07-01 03:03:21.213 # no non-identical "pretty close" pair
2025-07-01 03:03:21.218 if eqi is None:
2025-07-01 03:03:21.222 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:21.227 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:21.231 return
2025-07-01 03:03:21.236 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:21.241 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:21.245 else:
2025-07-01 03:03:21.250 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:21.255 eqi = None
2025-07-01 03:03:21.260
2025-07-01 03:03:21.265 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:21.271 # identical
2025-07-01 03:03:21.277
2025-07-01 03:03:21.283 # pump out diffs from before the synch point
2025-07-01 03:03:21.289 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:21.294
2025-07-01 03:03:21.300 # do intraline marking on the synch pair
2025-07-01 03:03:21.306 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:21.313 if eqi is None:
2025-07-01 03:03:21.318 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:21.322 atags = btags = ""
2025-07-01 03:03:21.327 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:21.331 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:21.336 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:21.340 if tag == 'replace':
2025-07-01 03:03:21.345 atags += '^' * la
2025-07-01 03:03:21.349 btags += '^' * lb
2025-07-01 03:03:21.353 elif tag == 'delete':
2025-07-01 03:03:21.358 atags += '-' * la
2025-07-01 03:03:21.362 elif tag == 'insert':
2025-07-01 03:03:21.366 btags += '+' * lb
2025-07-01 03:03:21.370 elif tag == 'equal':
2025-07-01 03:03:21.375 atags += ' ' * la
2025-07-01 03:03:21.379 btags += ' ' * lb
2025-07-01 03:03:21.384 else:
2025-07-01 03:03:21.388 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:21.393 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:21.397 else:
2025-07-01 03:03:21.401 # the synch pair is identical
2025-07-01 03:03:21.406 yield '  ' + aelt
2025-07-01 03:03:21.410
2025-07-01 03:03:21.415 # pump out diffs from after the synch point
2025-07-01 03:03:21.419 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:21.423
2025-07-01 03:03:21.428 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:21.433 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:21.437
2025-07-01 03:03:21.442 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:21.446 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:21.451 alo = 309, ahi = 1101
2025-07-01 03:03:21.455 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:21.460 blo = 309, bhi = 1101
2025-07-01 03:03:21.464
2025-07-01 03:03:21.468 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:21.473 g = []
2025-07-01 03:03:21.477 if alo < ahi:
2025-07-01 03:03:21.481 if blo < bhi:
2025-07-01 03:03:21.486 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:21.490 else:
2025-07-01 03:03:21.495 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:21.499 elif blo < bhi:
2025-07-01 03:03:21.503 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:21.508
2025-07-01 03:03:21.512 >       yield from g
2025-07-01 03:03:21.516
2025-07-01 03:03:21.520 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:21.525 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:21.529
2025-07-01 03:03:21.533 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:21.538 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:21.543 alo = 309, ahi = 1101
2025-07-01 03:03:21.548 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:21.552 blo = 309, bhi = 1101
2025-07-01 03:03:21.556
2025-07-01 03:03:21.561 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:21.565 r"""
2025-07-01 03:03:21.569 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:21.574 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:21.578 synch point, and intraline difference marking is done on the
2025-07-01 03:03:21.582 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:21.587
2025-07-01 03:03:21.591 Example:
2025-07-01 03:03:21.595
2025-07-01 03:03:21.599 >>> d = Differ()
2025-07-01 03:03:21.604 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:21.609 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:21.613 >>> print(''.join(results), end="")
2025-07-01 03:03:21.617 - abcDefghiJkl
2025-07-01 03:03:21.626 + abcdefGhijkl
2025-07-01 03:03:21.635 """
2025-07-01 03:03:21.639
2025-07-01 03:03:21.643 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:21.648 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:21.652 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:21.657 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:21.661 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:21.665
2025-07-01 03:03:21.670 # search for the pair that matches best without being identical
2025-07-01 03:03:21.675 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:21.680 # on junk -- unless we have to)
2025-07-01 03:03:21.685 for j in range(blo, bhi):
2025-07-01 03:03:21.689 bj = b[j]
2025-07-01 03:03:21.695 cruncher.set_seq2(bj)
2025-07-01 03:03:21.699 for i in range(alo, ahi):
2025-07-01 03:03:21.704 ai = a[i]
2025-07-01 03:03:21.709 if ai == bj:
2025-07-01 03:03:21.713 if eqi is None:
2025-07-01 03:03:21.717 eqi, eqj = i, j
2025-07-01 03:03:21.722 continue
2025-07-01 03:03:21.726 cruncher.set_seq1(ai)
2025-07-01 03:03:21.731 # computing similarity is expensive, so use the quick
2025-07-01 03:03:21.735 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:21.740 # compares by a factor of 3.
2025-07-01 03:03:21.744 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:21.749 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:21.753 # of the computation is cached by cruncher
2025-07-01 03:03:21.758 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:21.763 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:21.767 cruncher.ratio() > best_ratio:
2025-07-01 03:03:21.773 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:21.777 if best_ratio < cutoff:
2025-07-01 03:03:21.782 # no non-identical "pretty close" pair
2025-07-01 03:03:21.786 if eqi is None:
2025-07-01 03:03:21.791 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:21.796 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:21.800 return
2025-07-01 03:03:21.805 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:21.810 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:21.814 else:
2025-07-01 03:03:21.819 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:21.823 eqi = None
2025-07-01 03:03:21.827
2025-07-01 03:03:21.832 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:21.836 # identical
2025-07-01 03:03:21.841
2025-07-01 03:03:21.845 # pump out diffs from before the synch point
2025-07-01 03:03:21.850 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:21.854
2025-07-01 03:03:21.859 # do intraline marking on the synch pair
2025-07-01 03:03:21.864 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:21.868 if eqi is None:
2025-07-01 03:03:21.873 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:21.877 atags = btags = ""
2025-07-01 03:03:21.882 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:21.887 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:21.891 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:21.896 if tag == 'replace':
2025-07-01 03:03:21.901 atags += '^' * la
2025-07-01 03:03:21.905 btags += '^' * lb
2025-07-01 03:03:21.910 elif tag == 'delete':
2025-07-01 03:03:21.914 atags += '-' * la
2025-07-01 03:03:21.919 elif tag == 'insert':
2025-07-01 03:03:21.924 btags += '+' * lb
2025-07-01 03:03:21.928 elif tag == 'equal':
2025-07-01 03:03:21.932 atags += ' ' * la
2025-07-01 03:03:21.937 btags += ' ' * lb
2025-07-01 03:03:21.941 else:
2025-07-01 03:03:21.945 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:21.950 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:21.954 else:
2025-07-01 03:03:21.959 # the synch pair is identical
2025-07-01 03:03:21.963 yield '  ' + aelt
2025-07-01 03:03:21.967
2025-07-01 03:03:21.972 # pump out diffs from after the synch point
2025-07-01 03:03:21.976 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:21.980
2025-07-01 03:03:21.985 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:21.990 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:21.996
2025-07-01 03:03:22.000 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:22.005 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:22.010 alo = 312, ahi = 1101
2025-07-01 03:03:22.015 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:22.020 blo = 312, bhi = 1101
2025-07-01 03:03:22.024
2025-07-01 03:03:22.029 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:22.034 g = []
2025-07-01 03:03:22.039 if alo < ahi:
2025-07-01 03:03:22.044 if blo < bhi:
2025-07-01 03:03:22.048 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:22.052 else:
2025-07-01 03:03:22.057 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:22.061 elif blo < bhi:
2025-07-01 03:03:22.065 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:22.070
2025-07-01 03:03:22.074 >       yield from g
2025-07-01 03:03:22.079
2025-07-01 03:03:22.083 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:22.088 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:22.092
2025-07-01 03:03:22.096 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:22.101 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:22.106 alo = 312, ahi = 1101
2025-07-01 03:03:22.110 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:22.115 blo = 312, bhi = 1101
2025-07-01 03:03:22.119
2025-07-01 03:03:22.124 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:22.128 r"""
2025-07-01 03:03:22.132 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:22.137 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:22.141 synch point, and intraline difference marking is done on the
2025-07-01 03:03:22.147 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:22.151
2025-07-01 03:03:22.156 Example:
2025-07-01 03:03:22.160
2025-07-01 03:03:22.164 >>> d = Differ()
2025-07-01 03:03:22.169 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:22.173 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:22.177 >>> print(''.join(results), end="")
2025-07-01 03:03:22.182 - abcDefghiJkl
2025-07-01 03:03:22.191 + abcdefGhijkl
2025-07-01 03:03:22.200 """
2025-07-01 03:03:22.205
2025-07-01 03:03:22.209 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:22.214 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:22.218 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:22.223 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:22.227 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:22.231
2025-07-01 03:03:22.236 # search for the pair that matches best without being identical
2025-07-01 03:03:22.240 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:22.244 # on junk -- unless we have to)
2025-07-01 03:03:22.249 for j in range(blo, bhi):
2025-07-01 03:03:22.253 bj = b[j]
2025-07-01 03:03:22.258 cruncher.set_seq2(bj)
2025-07-01 03:03:22.262 for i in range(alo, ahi):
2025-07-01 03:03:22.266 ai = a[i]
2025-07-01 03:03:22.271 if ai == bj:
2025-07-01 03:03:22.275 if eqi is None:
2025-07-01 03:03:22.280 eqi, eqj = i, j
2025-07-01 03:03:22.284 continue
2025-07-01 03:03:22.288 cruncher.set_seq1(ai)
2025-07-01 03:03:22.293 # computing similarity is expensive, so use the quick
2025-07-01 03:03:22.297 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:22.302 # compares by a factor of 3.
2025-07-01 03:03:22.307 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:22.312 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:22.317 # of the computation is cached by cruncher
2025-07-01 03:03:22.321 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:22.326 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:22.331 cruncher.ratio() > best_ratio:
2025-07-01 03:03:22.337 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:22.341 if best_ratio < cutoff:
2025-07-01 03:03:22.347 # no non-identical "pretty close" pair
2025-07-01 03:03:22.352 if eqi is None:
2025-07-01 03:03:22.356 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:22.361 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:22.366 return
2025-07-01 03:03:22.371 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:22.376 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:22.380 else:
2025-07-01 03:03:22.385 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:22.391 eqi = None
2025-07-01 03:03:22.397
2025-07-01 03:03:22.404 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:22.411 # identical
2025-07-01 03:03:22.417
2025-07-01 03:03:22.424 # pump out diffs from before the synch point
2025-07-01 03:03:22.430 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:22.435
2025-07-01 03:03:22.441 # do intraline marking on the synch pair
2025-07-01 03:03:22.445 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:22.449 if eqi is None:
2025-07-01 03:03:22.454 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:22.458 atags = btags = ""
2025-07-01 03:03:22.462 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:22.467 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:22.471 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:22.475 if tag == 'replace':
2025-07-01 03:03:22.480 atags += '^' * la
2025-07-01 03:03:22.484 btags += '^' * lb
2025-07-01 03:03:22.489 elif tag == 'delete':
2025-07-01 03:03:22.493 atags += '-' * la
2025-07-01 03:03:22.498 elif tag == 'insert':
2025-07-01 03:03:22.502 btags += '+' * lb
2025-07-01 03:03:22.506 elif tag == 'equal':
2025-07-01 03:03:22.511 atags += ' ' * la
2025-07-01 03:03:22.515 btags += ' ' * lb
2025-07-01 03:03:22.520 else:
2025-07-01 03:03:22.524 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:22.529 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:22.534 else:
2025-07-01 03:03:22.538 # the synch pair is identical
2025-07-01 03:03:22.542 yield '  ' + aelt
2025-07-01 03:03:22.546
2025-07-01 03:03:22.551 # pump out diffs from after the synch point
2025-07-01 03:03:22.555 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:22.559
2025-07-01 03:03:22.564 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:22.568 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:22.572
2025-07-01 03:03:22.577 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:22.581 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:22.586 alo = 313, ahi = 1101
2025-07-01 03:03:22.591 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:22.595 blo = 313, bhi = 1101
2025-07-01 03:03:22.599
2025-07-01 03:03:22.604 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:22.608 g = []
2025-07-01 03:03:22.612 if alo < ahi:
2025-07-01 03:03:22.617 if blo < bhi:
2025-07-01 03:03:22.621 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:22.625 else:
2025-07-01 03:03:22.630 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:22.635 elif blo < bhi:
2025-07-01 03:03:22.639 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:22.643
2025-07-01 03:03:22.647 >       yield from g
2025-07-01 03:03:22.652
2025-07-01 03:03:22.656 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:22.661 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:22.665
2025-07-01 03:03:22.669 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:22.674 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:22.679 alo = 313, ahi = 1101
2025-07-01 03:03:22.683 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:22.688 blo = 313, bhi = 1101
2025-07-01 03:03:22.692
2025-07-01 03:03:22.697 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:22.701 r"""
2025-07-01 03:03:22.706 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:22.710 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:22.715 synch point, and intraline difference marking is done on the
2025-07-01 03:03:22.720 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:22.725
2025-07-01 03:03:22.730 Example:
2025-07-01 03:03:22.735
2025-07-01 03:03:22.741 >>> d = Differ()
2025-07-01 03:03:22.747 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:22.752 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:22.756 >>> print(''.join(results), end="")
2025-07-01 03:03:22.761 - abcDefghiJkl
2025-07-01 03:03:22.769 + abcdefGhijkl
2025-07-01 03:03:22.778 """
2025-07-01 03:03:22.782
2025-07-01 03:03:22.787 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:22.794 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:22.799 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:22.804 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:22.810 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:22.815
2025-07-01 03:03:22.821 # search for the pair that matches best without being identical
2025-07-01 03:03:22.827 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:22.832 # on junk -- unless we have to)
2025-07-01 03:03:22.838 for j in range(blo, bhi):
2025-07-01 03:03:22.843 bj = b[j]
2025-07-01 03:03:22.848 cruncher.set_seq2(bj)
2025-07-01 03:03:22.853 for i in range(alo, ahi):
2025-07-01 03:03:22.859 ai = a[i]
2025-07-01 03:03:22.864 if ai == bj:
2025-07-01 03:03:22.869 if eqi is None:
2025-07-01 03:03:22.874 eqi, eqj = i, j
2025-07-01 03:03:22.879 continue
2025-07-01 03:03:22.884 cruncher.set_seq1(ai)
2025-07-01 03:03:22.890 # computing similarity is expensive, so use the quick
2025-07-01 03:03:22.895 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:22.900 # compares by a factor of 3.
2025-07-01 03:03:22.906 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:22.911 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:22.917 # of the computation is cached by cruncher
2025-07-01 03:03:22.922 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:22.927 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:22.933 cruncher.ratio() > best_ratio:
2025-07-01 03:03:22.939 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:22.944 if best_ratio < cutoff:
2025-07-01 03:03:22.949 # no non-identical "pretty close" pair
2025-07-01 03:03:22.953 if eqi is None:
2025-07-01 03:03:22.958 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:22.962 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:22.968 return
2025-07-01 03:03:22.973 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:22.978 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:22.983 else:
2025-07-01 03:03:22.987 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:22.991 eqi = None
2025-07-01 03:03:22.996
2025-07-01 03:03:23.001 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:23.006 # identical
2025-07-01 03:03:23.010
2025-07-01 03:03:23.015 # pump out diffs from before the synch point
2025-07-01 03:03:23.020 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:23.025
2025-07-01 03:03:23.033 # do intraline marking on the synch pair
2025-07-01 03:03:23.040 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:23.046 if eqi is None:
2025-07-01 03:03:23.052 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:23.057 atags = btags = ""
2025-07-01 03:03:23.062 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:23.066 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:23.071 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:23.076 if tag == 'replace':
2025-07-01 03:03:23.081 atags += '^' * la
2025-07-01 03:03:23.086 btags += '^' * lb
2025-07-01 03:03:23.090 elif tag == 'delete':
2025-07-01 03:03:23.095 atags += '-' * la
2025-07-01 03:03:23.100 elif tag == 'insert':
2025-07-01 03:03:23.105 btags += '+' * lb
2025-07-01 03:03:23.113 elif tag == 'equal':
2025-07-01 03:03:23.120 atags += ' ' * la
2025-07-01 03:03:23.127 btags += ' ' * lb
2025-07-01 03:03:23.133 else:
2025-07-01 03:03:23.139 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:23.148 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:23.155 else:
2025-07-01 03:03:23.163 # the synch pair is identical
2025-07-01 03:03:23.170 yield '  ' + aelt
2025-07-01 03:03:23.177
2025-07-01 03:03:23.183 # pump out diffs from after the synch point
2025-07-01 03:03:23.189 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:23.194
2025-07-01 03:03:23.199 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:23.204 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:23.210
2025-07-01 03:03:23.216 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:23.223 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:23.228 alo = 314, ahi = 1101
2025-07-01 03:03:23.234 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:23.239 blo = 314, bhi = 1101
2025-07-01 03:03:23.244
2025-07-01 03:03:23.249 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:23.256 g = []
2025-07-01 03:03:23.264 if alo < ahi:
2025-07-01 03:03:23.271 if blo < bhi:
2025-07-01 03:03:23.277 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:23.281 else:
2025-07-01 03:03:23.289 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:23.297 elif blo < bhi:
2025-07-01 03:03:23.304 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:23.309
2025-07-01 03:03:23.314 >       yield from g
2025-07-01 03:03:23.320
2025-07-01 03:03:23.333 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:23.342 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:23.353
2025-07-01 03:03:23.362 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:23.371 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:23.380 alo = 314, ahi = 1101
2025-07-01 03:03:23.388 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:23.396 blo = 314, bhi = 1101
2025-07-01 03:03:23.406
2025-07-01 03:03:23.414 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:23.421 r"""
2025-07-01 03:03:23.429 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:23.439 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:23.449 synch point, and intraline difference marking is done on the
2025-07-01 03:03:23.459 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:23.465
2025-07-01 03:03:23.471 Example:
2025-07-01 03:03:23.478
2025-07-01 03:03:23.484 >>> d = Differ()
2025-07-01 03:03:23.490 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:23.498 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:23.510 >>> print(''.join(results), end="")
2025-07-01 03:03:23.519 - abcDefghiJkl
2025-07-01 03:03:23.533 + abcdefGhijkl
2025-07-01 03:03:23.548 """
2025-07-01 03:03:23.558
2025-07-01 03:03:23.570 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:23.580 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:23.590 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:23.599 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:23.605 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:23.615
2025-07-01 03:03:23.627 # search for the pair that matches best without being identical
2025-07-01 03:03:23.636 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:23.644 # on junk -- unless we have to)
2025-07-01 03:03:23.651 for j in range(blo, bhi):
2025-07-01 03:03:23.656 bj = b[j]
2025-07-01 03:03:23.666 cruncher.set_seq2(bj)
2025-07-01 03:03:23.676 for i in range(alo, ahi):
2025-07-01 03:03:23.686 ai = a[i]
2025-07-01 03:03:23.693 if ai == bj:
2025-07-01 03:03:23.701 if eqi is None:
2025-07-01 03:03:23.708 eqi, eqj = i, j
2025-07-01 03:03:23.714 continue
2025-07-01 03:03:23.720 cruncher.set_seq1(ai)
2025-07-01 03:03:23.725 # computing similarity is expensive, so use the quick
2025-07-01 03:03:23.734 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:23.740 # compares by a factor of 3.
2025-07-01 03:03:23.747 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:23.754 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:23.760 # of the computation is cached by cruncher
2025-07-01 03:03:23.765 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:23.770 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:23.776 cruncher.ratio() > best_ratio:
2025-07-01 03:03:23.781 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:23.786 if best_ratio < cutoff:
2025-07-01 03:03:23.791 # no non-identical "pretty close" pair
2025-07-01 03:03:23.803 if eqi is None:
2025-07-01 03:03:23.811 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:23.818 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:23.825 return
2025-07-01 03:03:23.833 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:23.840 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:23.845 else:
2025-07-01 03:03:23.856 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:23.865 eqi = None
2025-07-01 03:03:23.871
2025-07-01 03:03:23.877 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:23.883 # identical
2025-07-01 03:03:23.888
2025-07-01 03:03:23.896 # pump out diffs from before the synch point
2025-07-01 03:03:23.906 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:23.914
2025-07-01 03:03:23.921 # do intraline marking on the synch pair
2025-07-01 03:03:23.927 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:23.939 if eqi is None:
2025-07-01 03:03:23.949 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:23.958 atags = btags = ""
2025-07-01 03:03:23.964 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:23.971 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:23.977 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:23.983 if tag == 'replace':
2025-07-01 03:03:23.988 atags += '^' * la
2025-07-01 03:03:23.993 btags += '^' * lb
2025-07-01 03:03:23.998 elif tag == 'delete':
2025-07-01 03:03:24.003 atags += '-' * la
2025-07-01 03:03:24.008 elif tag == 'insert':
2025-07-01 03:03:24.014 btags += '+' * lb
2025-07-01 03:03:24.019 elif tag == 'equal':
2025-07-01 03:03:24.024 atags += ' ' * la
2025-07-01 03:03:24.029 btags += ' ' * lb
2025-07-01 03:03:24.034 else:
2025-07-01 03:03:24.039 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:24.045 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:24.049 else:
2025-07-01 03:03:24.054 # the synch pair is identical
2025-07-01 03:03:24.059 yield '  ' + aelt
2025-07-01 03:03:24.064
2025-07-01 03:03:24.069 # pump out diffs from after the synch point
2025-07-01 03:03:24.077 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:24.082
2025-07-01 03:03:24.088 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:24.094 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:24.099
2025-07-01 03:03:24.109 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:24.118 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:24.123 alo = 315, ahi = 1101
2025-07-01 03:03:24.128 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:24.133 blo = 315, bhi = 1101
2025-07-01 03:03:24.139
2025-07-01 03:03:24.144 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:24.149 g = []
2025-07-01 03:03:24.155 if alo < ahi:
2025-07-01 03:03:24.159 if blo < bhi:
2025-07-01 03:03:24.164 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:24.169 else:
2025-07-01 03:03:24.177 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:24.182 elif blo < bhi:
2025-07-01 03:03:24.187 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:24.194
2025-07-01 03:03:24.199 >       yield from g
2025-07-01 03:03:24.204
2025-07-01 03:03:24.209 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:24.222 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:24.230
2025-07-01 03:03:24.237 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:24.242 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:24.247 alo = 315, ahi = 1101
2025-07-01 03:03:24.253 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:24.258 blo = 315, bhi = 1101
2025-07-01 03:03:24.263
2025-07-01 03:03:24.269 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:24.274 r"""
2025-07-01 03:03:24.279 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:24.285 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:24.294 synch point, and intraline difference marking is done on the
2025-07-01 03:03:24.301 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:24.311
2025-07-01 03:03:24.318 Example:
2025-07-01 03:03:24.324
2025-07-01 03:03:24.331 >>> d = Differ()
2025-07-01 03:03:24.335 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:24.343 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:24.354 >>> print(''.join(results), end="")
2025-07-01 03:03:24.362 - abcDefghiJkl
2025-07-01 03:03:24.373 + abcdefGhijkl
2025-07-01 03:03:24.383 """
2025-07-01 03:03:24.389
2025-07-01 03:03:24.395 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:24.401 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:24.407 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:24.413 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:24.419 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:24.425
2025-07-01 03:03:24.431 # search for the pair that matches best without being identical
2025-07-01 03:03:24.436 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:24.442 # on junk -- unless we have to)
2025-07-01 03:03:24.446 for j in range(blo, bhi):
2025-07-01 03:03:24.450 bj = b[j]
2025-07-01 03:03:24.455 cruncher.set_seq2(bj)
2025-07-01 03:03:24.461 for i in range(alo, ahi):
2025-07-01 03:03:24.474 ai = a[i]
2025-07-01 03:03:24.478 if ai == bj:
2025-07-01 03:03:24.483 if eqi is None:
2025-07-01 03:03:24.487 eqi, eqj = i, j
2025-07-01 03:03:24.493 continue
2025-07-01 03:03:24.498 cruncher.set_seq1(ai)
2025-07-01 03:03:24.503 # computing similarity is expensive, so use the quick
2025-07-01 03:03:24.508 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:24.513 # compares by a factor of 3.
2025-07-01 03:03:24.518 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:24.522 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:24.527 # of the computation is cached by cruncher
2025-07-01 03:03:24.531 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:24.535 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:24.540 cruncher.ratio() > best_ratio:
2025-07-01 03:03:24.544 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:24.549 if best_ratio < cutoff:
2025-07-01 03:03:24.553 # no non-identical "pretty close" pair
2025-07-01 03:03:24.557 if eqi is None:
2025-07-01 03:03:24.562 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:24.566 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:24.571 return
2025-07-01 03:03:24.575 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:24.580 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:24.584 else:
2025-07-01 03:03:24.588 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:24.592 eqi = None
2025-07-01 03:03:24.597
2025-07-01 03:03:24.601 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:24.606 # identical
2025-07-01 03:03:24.610
2025-07-01 03:03:24.614 # pump out diffs from before the synch point
2025-07-01 03:03:24.619 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:24.623
2025-07-01 03:03:24.628 # do intraline marking on the synch pair
2025-07-01 03:03:24.632 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:24.636 if eqi is None:
2025-07-01 03:03:24.641 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:24.646 atags = btags = ""
2025-07-01 03:03:24.650 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:24.655 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:24.659 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:24.663 if tag == 'replace':
2025-07-01 03:03:24.668 atags += '^' * la
2025-07-01 03:03:24.672 btags += '^' * lb
2025-07-01 03:03:24.676 elif tag == 'delete':
2025-07-01 03:03:24.682 atags += '-' * la
2025-07-01 03:03:24.686 elif tag == 'insert':
2025-07-01 03:03:24.691 btags += '+' * lb
2025-07-01 03:03:24.695 elif tag == 'equal':
2025-07-01 03:03:24.699 atags += ' ' * la
2025-07-01 03:03:24.703 btags += ' ' * lb
2025-07-01 03:03:24.708 else:
2025-07-01 03:03:24.713 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:24.717 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:24.721 else:
2025-07-01 03:03:24.726 # the synch pair is identical
2025-07-01 03:03:24.730 yield '  ' + aelt
2025-07-01 03:03:24.735
2025-07-01 03:03:24.741 # pump out diffs from after the synch point
2025-07-01 03:03:24.746 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:24.751
2025-07-01 03:03:24.755 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:24.761 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:24.766
2025-07-01 03:03:24.770 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:24.776 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:24.780 alo = 316, ahi = 1101
2025-07-01 03:03:24.785 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:24.789 blo = 316, bhi = 1101
2025-07-01 03:03:24.793
2025-07-01 03:03:24.799 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:24.804 g = []
2025-07-01 03:03:24.809 if alo < ahi:
2025-07-01 03:03:24.813 if blo < bhi:
2025-07-01 03:03:24.818 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:24.824 else:
2025-07-01 03:03:24.830 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:24.836 elif blo < bhi:
2025-07-01 03:03:24.841 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:24.845
2025-07-01 03:03:24.850 >       yield from g
2025-07-01 03:03:24.856
2025-07-01 03:03:24.860 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:24.865 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:24.869
2025-07-01 03:03:24.874 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:24.882 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:24.890 alo = 316, ahi = 1101
2025-07-01 03:03:24.902 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:24.918 blo = 316, bhi = 1101
2025-07-01 03:03:24.930
2025-07-01 03:03:24.942 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:24.954 r"""
2025-07-01 03:03:24.962 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:24.968 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:24.986 synch point, and intraline difference marking is done on the
2025-07-01 03:03:24.998 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:25.010
2025-07-01 03:03:25.022 Example:
2025-07-01 03:03:25.030
2025-07-01 03:03:25.042 >>> d = Differ()
2025-07-01 03:03:25.058 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:25.066 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:25.073 >>> print(''.join(results), end="")
2025-07-01 03:03:25.086 - abcDefghiJkl
2025-07-01 03:03:25.114 + abcdefGhijkl
2025-07-01 03:03:25.134 """
2025-07-01 03:03:25.146
2025-07-01 03:03:25.158 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:25.169 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:25.182 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:25.190 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:25.202 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:25.214
2025-07-01 03:03:25.234 # search for the pair that matches best without being identical
2025-07-01 03:03:25.241 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:25.257 # on junk -- unless we have to)
2025-07-01 03:03:25.266 for j in range(blo, bhi):
2025-07-01 03:03:25.282 bj = b[j]
2025-07-01 03:03:25.290 cruncher.set_seq2(bj)
2025-07-01 03:03:25.302 for i in range(alo, ahi):
2025-07-01 03:03:25.310 ai = a[i]
2025-07-01 03:03:25.318 if ai == bj:
2025-07-01 03:03:25.330 if eqi is None:
2025-07-01 03:03:25.337 eqi, eqj = i, j
2025-07-01 03:03:25.354 continue
2025-07-01 03:03:25.366 cruncher.set_seq1(ai)
2025-07-01 03:03:25.375 # computing similarity is expensive, so use the quick
2025-07-01 03:03:25.380 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:25.384 # compares by a factor of 3.
2025-07-01 03:03:25.389 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:25.393 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:25.398 # of the computation is cached by cruncher
2025-07-01 03:03:25.402 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:25.407 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:25.411 cruncher.ratio() > best_ratio:
2025-07-01 03:03:25.416 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:25.421 if best_ratio < cutoff:
2025-07-01 03:03:25.426 # no non-identical "pretty close" pair
2025-07-01 03:03:25.430 if eqi is None:
2025-07-01 03:03:25.435 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:25.440 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:25.444 return
2025-07-01 03:03:25.449 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:25.454 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:25.460 else:
2025-07-01 03:03:25.465 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:25.470 eqi = None
2025-07-01 03:03:25.474
2025-07-01 03:03:25.479 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:25.483 # identical
2025-07-01 03:03:25.488
2025-07-01 03:03:25.492 # pump out diffs from before the synch point
2025-07-01 03:03:25.497 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:25.502
2025-07-01 03:03:25.506 # do intraline marking on the synch pair
2025-07-01 03:03:25.511 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:25.515 if eqi is None:
2025-07-01 03:03:25.520 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:25.525 atags = btags = ""
2025-07-01 03:03:25.529 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:25.534 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:25.538 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:25.543 if tag == 'replace':
2025-07-01 03:03:25.547 atags += '^' * la
2025-07-01 03:03:25.552 btags += '^' * lb
2025-07-01 03:03:25.557 elif tag == 'delete':
2025-07-01 03:03:25.561 atags += '-' * la
2025-07-01 03:03:25.566 elif tag == 'insert':
2025-07-01 03:03:25.570 btags += '+' * lb
2025-07-01 03:03:25.575 elif tag == 'equal':
2025-07-01 03:03:25.579 atags += ' ' * la
2025-07-01 03:03:25.583 btags += ' ' * lb
2025-07-01 03:03:25.588 else:
2025-07-01 03:03:25.592 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:25.598 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:25.603 else:
2025-07-01 03:03:25.609 # the synch pair is identical
2025-07-01 03:03:25.615 yield '  ' + aelt
2025-07-01 03:03:25.620
2025-07-01 03:03:25.626 # pump out diffs from after the synch point
2025-07-01 03:03:25.632 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:25.638
2025-07-01 03:03:25.644 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:25.649 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:25.653
2025-07-01 03:03:25.658 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:25.662 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:25.667 alo = 317, ahi = 1101
2025-07-01 03:03:25.671 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:25.676 blo = 317, bhi = 1101
2025-07-01 03:03:25.680
2025-07-01 03:03:25.684 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:25.689 g = []
2025-07-01 03:03:25.693 if alo < ahi:
2025-07-01 03:03:25.706 if blo < bhi:
2025-07-01 03:03:25.722 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:25.730 else:
2025-07-01 03:03:25.742 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:25.754 elif blo < bhi:
2025-07-01 03:03:25.770 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:25.782
2025-07-01 03:03:25.793 >       yield from g
2025-07-01 03:03:25.805
2025-07-01 03:03:25.814 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:25.826 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:25.837
2025-07-01 03:03:25.846 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:25.858 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:25.870 alo = 317, ahi = 1101
2025-07-01 03:03:25.886 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:25.894 blo = 317, bhi = 1101
2025-07-01 03:03:25.906
2025-07-01 03:03:25.918 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:25.924 r"""
2025-07-01 03:03:25.929 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:25.934 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:25.938 synch point, and intraline difference marking is done on the
2025-07-01 03:03:25.943 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:25.948
2025-07-01 03:03:25.953 Example:
2025-07-01 03:03:25.957
2025-07-01 03:03:25.962 >>> d = Differ()
2025-07-01 03:03:25.967 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:25.972 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:25.976 >>> print(''.join(results), end="")
2025-07-01 03:03:25.981 - abcDefghiJkl
2025-07-01 03:03:25.990 + abcdefGhijkl
2025-07-01 03:03:25.998 """
2025-07-01 03:03:26.003
2025-07-01 03:03:26.008 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:26.013 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:26.019 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:26.024 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:26.028 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:26.033
2025-07-01 03:03:26.037 # search for the pair that matches best without being identical
2025-07-01 03:03:26.042 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:26.046 # on junk -- unless we have to)
2025-07-01 03:03:26.051 for j in range(blo, bhi):
2025-07-01 03:03:26.055 bj = b[j]
2025-07-01 03:03:26.059 cruncher.set_seq2(bj)
2025-07-01 03:03:26.064 for i in range(alo, ahi):
2025-07-01 03:03:26.068 ai = a[i]
2025-07-01 03:03:26.073 if ai == bj:
2025-07-01 03:03:26.077 if eqi is None:
2025-07-01 03:03:26.082 eqi, eqj = i, j
2025-07-01 03:03:26.086 continue
2025-07-01 03:03:26.090 cruncher.set_seq1(ai)
2025-07-01 03:03:26.095 # computing similarity is expensive, so use the quick
2025-07-01 03:03:26.099 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:26.103 # compares by a factor of 3.
2025-07-01 03:03:26.108 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:26.113 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:26.117 # of the computation is cached by cruncher
2025-07-01 03:03:26.125 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:26.134 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:26.145 cruncher.ratio() > best_ratio:
2025-07-01 03:03:26.155 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:26.174 if best_ratio < cutoff:
2025-07-01 03:03:26.184 # no non-identical "pretty close" pair
2025-07-01 03:03:26.194 if eqi is None:
2025-07-01 03:03:26.206 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:26.214 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:26.224 return
2025-07-01 03:03:26.242 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:26.257 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:26.269 else:
2025-07-01 03:03:26.277 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:26.290 eqi = None
2025-07-01 03:03:26.302
2025-07-01 03:03:26.312 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:26.322 # identical
2025-07-01 03:03:26.334
2025-07-01 03:03:26.344 # pump out diffs from before the synch point
2025-07-01 03:03:26.358 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:26.366
2025-07-01 03:03:26.378 # do intraline marking on the synch pair
2025-07-01 03:03:26.390 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:26.402 if eqi is None:
2025-07-01 03:03:26.418 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:26.426 atags = btags = ""
2025-07-01 03:03:26.442 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:26.454 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:26.465 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:26.478 if tag == 'replace':
2025-07-01 03:03:26.491 atags += '^' * la
2025-07-01 03:03:26.508 btags += '^' * lb
2025-07-01 03:03:26.521 elif tag == 'delete':
2025-07-01 03:03:26.529 atags += '-' * la
2025-07-01 03:03:26.545 elif tag == 'insert':
2025-07-01 03:03:26.562 btags += '+' * lb
2025-07-01 03:03:26.577 elif tag == 'equal':
2025-07-01 03:03:26.590 atags += ' ' * la
2025-07-01 03:03:26.601 btags += ' ' * lb
2025-07-01 03:03:26.616 else:
2025-07-01 03:03:26.626 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:26.636 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:26.650 else:
2025-07-01 03:03:26.658 # the synch pair is identical
2025-07-01 03:03:26.670 yield '  ' + aelt
2025-07-01 03:03:26.682
2025-07-01 03:03:26.698 # pump out diffs from after the synch point
2025-07-01 03:03:26.710 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:26.722
2025-07-01 03:03:26.730 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:26.742 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:26.754
2025-07-01 03:03:26.762 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:26.778 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:26.786 alo = 318, ahi = 1101
2025-07-01 03:03:26.802 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:26.814 blo = 318, bhi = 1101
2025-07-01 03:03:26.826
2025-07-01 03:03:26.838 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:26.850 g = []
2025-07-01 03:03:26.862 if alo < ahi:
2025-07-01 03:03:26.874 if blo < bhi:
2025-07-01 03:03:26.890 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:26.898 else:
2025-07-01 03:03:26.909 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:26.927 elif blo < bhi:
2025-07-01 03:03:26.949 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:26.958
2025-07-01 03:03:26.966 >       yield from g
2025-07-01 03:03:26.978
2025-07-01 03:03:26.986 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:27.005 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:27.014
2025-07-01 03:03:27.027 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:27.046 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:27.056 alo = 318, ahi = 1101
2025-07-01 03:03:27.066 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:27.082 blo = 318, bhi = 1101
2025-07-01 03:03:27.091
2025-07-01 03:03:27.102 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:27.107 r"""
2025-07-01 03:03:27.111 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:27.116 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:27.125 synch point, and intraline difference marking is done on the
2025-07-01 03:03:27.131 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:27.135
2025-07-01 03:03:27.140 Example:
2025-07-01 03:03:27.144
2025-07-01 03:03:27.148 >>> d = Differ()
2025-07-01 03:03:27.153 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:27.157 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:27.162 >>> print(''.join(results), end="")
2025-07-01 03:03:27.166 - abcDefghiJkl
2025-07-01 03:03:27.175 + abcdefGhijkl
2025-07-01 03:03:27.184 """
2025-07-01 03:03:27.189
2025-07-01 03:03:27.193 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:27.198 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:27.202 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:27.207 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:27.211 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:27.216
2025-07-01 03:03:27.220 # search for the pair that matches best without being identical
2025-07-01 03:03:27.225 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:27.229 # on junk -- unless we have to)
2025-07-01 03:03:27.234 for j in range(blo, bhi):
2025-07-01 03:03:27.238 bj = b[j]
2025-07-01 03:03:27.242 cruncher.set_seq2(bj)
2025-07-01 03:03:27.247 for i in range(alo, ahi):
2025-07-01 03:03:27.251 ai = a[i]
2025-07-01 03:03:27.255 if ai == bj:
2025-07-01 03:03:27.260 if eqi is None:
2025-07-01 03:03:27.264 eqi, eqj = i, j
2025-07-01 03:03:27.269 continue
2025-07-01 03:03:27.273 cruncher.set_seq1(ai)
2025-07-01 03:03:27.278 # computing similarity is expensive, so use the quick
2025-07-01 03:03:27.282 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:27.287 # compares by a factor of 3.
2025-07-01 03:03:27.291 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:27.296 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:27.300 # of the computation is cached by cruncher
2025-07-01 03:03:27.305 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:27.309 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:27.314 cruncher.ratio() > best_ratio:
2025-07-01 03:03:27.318 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:27.323 if best_ratio < cutoff:
2025-07-01 03:03:27.327 # no non-identical "pretty close" pair
2025-07-01 03:03:27.331 if eqi is None:
2025-07-01 03:03:27.336 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:27.340 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:27.345 return
2025-07-01 03:03:27.349 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:27.353 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:27.357 else:
2025-07-01 03:03:27.362 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:27.366 eqi = None
2025-07-01 03:03:27.370
2025-07-01 03:03:27.375 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:27.379 # identical
2025-07-01 03:03:27.383
2025-07-01 03:03:27.388 # pump out diffs from before the synch point
2025-07-01 03:03:27.392 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:27.397
2025-07-01 03:03:27.401 # do intraline marking on the synch pair
2025-07-01 03:03:27.405 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:27.410 if eqi is None:
2025-07-01 03:03:27.414 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:27.419 atags = btags = ""
2025-07-01 03:03:27.424 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:27.429 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:27.434 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:27.438 if tag == 'replace':
2025-07-01 03:03:27.443 atags += '^' * la
2025-07-01 03:03:27.447 btags += '^' * lb
2025-07-01 03:03:27.452 elif tag == 'delete':
2025-07-01 03:03:27.456 atags += '-' * la
2025-07-01 03:03:27.461 elif tag == 'insert':
2025-07-01 03:03:27.466 btags += '+' * lb
2025-07-01 03:03:27.471 elif tag == 'equal':
2025-07-01 03:03:27.476 atags += ' ' * la
2025-07-01 03:03:27.480 btags += ' ' * lb
2025-07-01 03:03:27.485 else:
2025-07-01 03:03:27.490 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:27.495 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:27.501 else:
2025-07-01 03:03:27.505 # the synch pair is identical
2025-07-01 03:03:27.510 yield '  ' + aelt
2025-07-01 03:03:27.515
2025-07-01 03:03:27.519 # pump out diffs from after the synch point
2025-07-01 03:03:27.524 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:27.529
2025-07-01 03:03:27.534 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:27.539 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:27.543
2025-07-01 03:03:27.547 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:27.552 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:27.556 alo = 319, ahi = 1101
2025-07-01 03:03:27.562 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:27.566 blo = 319, bhi = 1101
2025-07-01 03:03:27.571
2025-07-01 03:03:27.575 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:27.580 g = []
2025-07-01 03:03:27.584 if alo < ahi:
2025-07-01 03:03:27.588 if blo < bhi:
2025-07-01 03:03:27.593 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:27.597 else:
2025-07-01 03:03:27.601 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:27.606 elif blo < bhi:
2025-07-01 03:03:27.611 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:27.615
2025-07-01 03:03:27.619 >       yield from g
2025-07-01 03:03:27.624
2025-07-01 03:03:27.631 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:27.635 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:27.640
2025-07-01 03:03:27.645 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:27.650 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:27.655 alo = 319, ahi = 1101
2025-07-01 03:03:27.659 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:27.663 blo = 319, bhi = 1101
2025-07-01 03:03:27.667
2025-07-01 03:03:27.672 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:27.676 r"""
2025-07-01 03:03:27.681 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:27.686 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:27.690 synch point, and intraline difference marking is done on the
2025-07-01 03:03:27.695 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:27.699
2025-07-01 03:03:27.703 Example:
2025-07-01 03:03:27.708
2025-07-01 03:03:27.713 >>> d = Differ()
2025-07-01 03:03:27.719 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:27.724 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:27.728 >>> print(''.join(results), end="")
2025-07-01 03:03:27.733 - abcDefghiJkl
2025-07-01 03:03:27.741 + abcdefGhijkl
2025-07-01 03:03:27.751 """
2025-07-01 03:03:27.755
2025-07-01 03:03:27.760 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:27.765 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:27.770 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:27.774 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:27.779 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:27.783
2025-07-01 03:03:27.788 # search for the pair that matches best without being identical
2025-07-01 03:03:27.792 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:27.797 # on junk -- unless we have to)
2025-07-01 03:03:27.801 for j in range(blo, bhi):
2025-07-01 03:03:27.805 bj = b[j]
2025-07-01 03:03:27.810 cruncher.set_seq2(bj)
2025-07-01 03:03:27.814 for i in range(alo, ahi):
2025-07-01 03:03:27.819 ai = a[i]
2025-07-01 03:03:27.823 if ai == bj:
2025-07-01 03:03:27.828 if eqi is None:
2025-07-01 03:03:27.832 eqi, eqj = i, j
2025-07-01 03:03:27.837 continue
2025-07-01 03:03:27.841 cruncher.set_seq1(ai)
2025-07-01 03:03:27.849 # computing similarity is expensive, so use the quick
2025-07-01 03:03:27.874 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:27.883 # compares by a factor of 3.
2025-07-01 03:03:27.894 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:27.905 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:27.914 # of the computation is cached by cruncher
2025-07-01 03:03:27.925 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:27.933 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:27.945 cruncher.ratio() > best_ratio:
2025-07-01 03:03:27.953 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:27.962 if best_ratio < cutoff:
2025-07-01 03:03:27.974 # no non-identical "pretty close" pair
2025-07-01 03:03:27.982 if eqi is None:
2025-07-01 03:03:27.994 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:28.003 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:28.018 return
2025-07-01 03:03:28.026 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:28.039 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:28.050 else:
2025-07-01 03:03:28.058 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:28.072 eqi = None
2025-07-01 03:03:28.082
2025-07-01 03:03:28.095 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:28.109 # identical
2025-07-01 03:03:28.118
2025-07-01 03:03:28.129 # pump out diffs from before the synch point
2025-07-01 03:03:28.142 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:28.150
2025-07-01 03:03:28.158 # do intraline marking on the synch pair
2025-07-01 03:03:28.177 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:28.194 if eqi is None:
2025-07-01 03:03:28.206 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:28.214 atags = btags = ""
2025-07-01 03:03:28.226 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:28.238 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:28.250 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:28.262 if tag == 'replace':
2025-07-01 03:03:28.274 atags += '^' * la
2025-07-01 03:03:28.286 btags += '^' * lb
2025-07-01 03:03:28.294 elif tag == 'delete':
2025-07-01 03:03:28.308 atags += '-' * la
2025-07-01 03:03:28.322 elif tag == 'insert':
2025-07-01 03:03:28.332 btags += '+' * lb
2025-07-01 03:03:28.348 elif tag == 'equal':
2025-07-01 03:03:28.366 atags += ' ' * la
2025-07-01 03:03:28.374 btags += ' ' * lb
2025-07-01 03:03:28.386 else:
2025-07-01 03:03:28.400 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:28.409 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:28.418 else:
2025-07-01 03:03:28.429 # the synch pair is identical
2025-07-01 03:03:28.438 yield '  ' + aelt
2025-07-01 03:03:28.447
2025-07-01 03:03:28.454 # pump out diffs from after the synch point
2025-07-01 03:03:28.462 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:28.474
2025-07-01 03:03:28.487 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:28.498 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:28.512
2025-07-01 03:03:28.522 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:28.534 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:28.546 alo = 320, ahi = 1101
2025-07-01 03:03:28.558 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:28.570 blo = 320, bhi = 1101
2025-07-01 03:03:28.582
2025-07-01 03:03:28.590 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:28.602 g = []
2025-07-01 03:03:28.610 if alo < ahi:
2025-07-01 03:03:28.622 if blo < bhi:
2025-07-01 03:03:28.630 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:28.642 else:
2025-07-01 03:03:28.654 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:28.662 elif blo < bhi:
2025-07-01 03:03:28.674 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:28.686
2025-07-01 03:03:28.695 >       yield from g
2025-07-01 03:03:28.714
2025-07-01 03:03:28.722 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:28.730 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:28.742
2025-07-01 03:03:28.754 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:28.763 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:28.773 alo = 320, ahi = 1101
2025-07-01 03:03:28.788 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:28.797 blo = 320, bhi = 1101
2025-07-01 03:03:28.801
2025-07-01 03:03:28.805 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:28.812 r"""
2025-07-01 03:03:28.825 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:28.830 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:28.845 synch point, and intraline difference marking is done on the
2025-07-01 03:03:28.854 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:28.863
2025-07-01 03:03:28.879 Example:
2025-07-01 03:03:28.886
2025-07-01 03:03:28.900 >>> d = Differ()
2025-07-01 03:03:28.914 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:28.924 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:28.941 >>> print(''.join(results), end="")
2025-07-01 03:03:28.953 - abcDefghiJkl
2025-07-01 03:03:28.974 + abcdefGhijkl
2025-07-01 03:03:28.994 """
2025-07-01 03:03:29.006
2025-07-01 03:03:29.014 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:29.026 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:29.034 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:29.046 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:29.056 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:29.071
2025-07-01 03:03:29.084 # search for the pair that matches best without being identical
2025-07-01 03:03:29.096 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:29.103 # on junk -- unless we have to)
2025-07-01 03:03:29.110 for j in range(blo, bhi):
2025-07-01 03:03:29.124 bj = b[j]
2025-07-01 03:03:29.134 cruncher.set_seq2(bj)
2025-07-01 03:03:29.149 for i in range(alo, ahi):
2025-07-01 03:03:29.165 ai = a[i]
2025-07-01 03:03:29.177 if ai == bj:
2025-07-01 03:03:29.190 if eqi is None:
2025-07-01 03:03:29.197 eqi, eqj = i, j
2025-07-01 03:03:29.214 continue
2025-07-01 03:03:29.222 cruncher.set_seq1(ai)
2025-07-01 03:03:29.230 # computing similarity is expensive, so use the quick
2025-07-01 03:03:29.242 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:29.253 # compares by a factor of 3.
2025-07-01 03:03:29.261 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:29.273 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:29.281 # of the computation is cached by cruncher
2025-07-01 03:03:29.294 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:29.302 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:29.314 cruncher.ratio() > best_ratio:
2025-07-01 03:03:29.326 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:29.337 if best_ratio < cutoff:
2025-07-01 03:03:29.348 # no non-identical "pretty close" pair
2025-07-01 03:03:29.365 if eqi is None:
2025-07-01 03:03:29.378 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:29.385 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:29.391 return
2025-07-01 03:03:29.398 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:29.418 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:29.426 else:
2025-07-01 03:03:29.434 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:29.450 eqi = None
2025-07-01 03:03:29.458
2025-07-01 03:03:29.474 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:29.482 # identical
2025-07-01 03:03:29.494
2025-07-01 03:03:29.506 # pump out diffs from before the synch point
2025-07-01 03:03:29.524 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:29.534
2025-07-01 03:03:29.546 # do intraline marking on the synch pair
2025-07-01 03:03:29.553 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:29.570 if eqi is None:
2025-07-01 03:03:29.578 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:29.594 atags = btags = ""
2025-07-01 03:03:29.602 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:29.612 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:29.619 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:29.630 if tag == 'replace':
2025-07-01 03:03:29.646 atags += '^' * la
2025-07-01 03:03:29.662 btags += '^' * lb
2025-07-01 03:03:29.670 elif tag == 'delete':
2025-07-01 03:03:29.678 atags += '-' * la
2025-07-01 03:03:29.690 elif tag == 'insert':
2025-07-01 03:03:29.702 btags += '+' * lb
2025-07-01 03:03:29.713 elif tag == 'equal':
2025-07-01 03:03:29.726 atags += ' ' * la
2025-07-01 03:03:29.742 btags += ' ' * lb
2025-07-01 03:03:29.749 else:
2025-07-01 03:03:29.763 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:29.770 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:29.789 else:
2025-07-01 03:03:29.804 # the synch pair is identical
2025-07-01 03:03:29.814 yield '  ' + aelt
2025-07-01 03:03:29.822
2025-07-01 03:03:29.834 # pump out diffs from after the synch point
2025-07-01 03:03:29.841 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:29.854
2025-07-01 03:03:29.862 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:29.874 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:29.882
2025-07-01 03:03:29.890 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:29.902 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:29.914 alo = 321, ahi = 1101
2025-07-01 03:03:29.926 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:29.937 blo = 321, bhi = 1101
2025-07-01 03:03:29.945
2025-07-01 03:03:29.962 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:29.974 g = []
2025-07-01 03:03:29.986 if alo < ahi:
2025-07-01 03:03:29.998 if blo < bhi:
2025-07-01 03:03:30.010 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:30.030 else:
2025-07-01 03:03:30.042 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:30.054 elif blo < bhi:
2025-07-01 03:03:30.066 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:30.078
2025-07-01 03:03:30.085 >       yield from g
2025-07-01 03:03:30.102
2025-07-01 03:03:30.114 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:30.122 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:30.137
2025-07-01 03:03:30.145 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:30.161 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:30.169 alo = 321, ahi = 1101
2025-07-01 03:03:30.180 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:30.186 blo = 321, bhi = 1101
2025-07-01 03:03:30.193
2025-07-01 03:03:30.205 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:30.214 r"""
2025-07-01 03:03:30.222 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:30.237 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:30.246 synch point, and intraline difference marking is done on the
2025-07-01 03:03:30.258 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:30.266
2025-07-01 03:03:30.278 Example:
2025-07-01 03:03:30.290
2025-07-01 03:03:30.298 >>> d = Differ()
2025-07-01 03:03:30.313 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:30.326 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:30.334 >>> print(''.join(results), end="")
2025-07-01 03:03:30.345 - abcDefghiJkl
2025-07-01 03:03:30.366 + abcdefGhijkl
2025-07-01 03:03:30.386 """
2025-07-01 03:03:30.398
2025-07-01 03:03:30.405 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:30.418 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:30.426 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:30.438 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:30.445 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:30.453
2025-07-01 03:03:30.466 # search for the pair that matches best without being identical
2025-07-01 03:03:30.473 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:30.485 # on junk -- unless we have to)
2025-07-01 03:03:30.494 for j in range(blo, bhi):
2025-07-01 03:03:30.506 bj = b[j]
2025-07-01 03:03:30.514 cruncher.set_seq2(bj)
2025-07-01 03:03:30.526 for i in range(alo, ahi):
2025-07-01 03:03:30.534 ai = a[i]
2025-07-01 03:03:30.550 if ai == bj:
2025-07-01 03:03:30.559 if eqi is None:
2025-07-01 03:03:30.574 eqi, eqj = i, j
2025-07-01 03:03:30.585 continue
2025-07-01 03:03:30.593 cruncher.set_seq1(ai)
2025-07-01 03:03:30.610 # computing similarity is expensive, so use the quick
2025-07-01 03:03:30.618 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:30.630 # compares by a factor of 3.
2025-07-01 03:03:30.646 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:30.653 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:30.665 # of the computation is cached by cruncher
2025-07-01 03:03:30.677 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:30.694 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:30.701 cruncher.ratio() > best_ratio:
2025-07-01 03:03:30.714 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:30.722 if best_ratio < cutoff:
2025-07-01 03:03:30.738 # no non-identical "pretty close" pair
2025-07-01 03:03:30.745 if eqi is None:
2025-07-01 03:03:30.757 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:30.770 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:30.781 return
2025-07-01 03:03:30.789 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:30.804 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:30.813 else:
2025-07-01 03:03:30.825 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:30.838 eqi = None
2025-07-01 03:03:30.844
2025-07-01 03:03:30.848 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:30.852 # identical
2025-07-01 03:03:30.857
2025-07-01 03:03:30.861 # pump out diffs from before the synch point
2025-07-01 03:03:30.866 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:30.870
2025-07-01 03:03:30.874 # do intraline marking on the synch pair
2025-07-01 03:03:30.878 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:30.883 if eqi is None:
2025-07-01 03:03:30.887 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:30.891 atags = btags = ""
2025-07-01 03:03:30.896 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:30.900 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:30.905 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:30.909 if tag == 'replace':
2025-07-01 03:03:30.914 atags += '^' * la
2025-07-01 03:03:30.918 btags += '^' * lb
2025-07-01 03:03:30.923 elif tag == 'delete':
2025-07-01 03:03:30.927 atags += '-' * la
2025-07-01 03:03:30.932 elif tag == 'insert':
2025-07-01 03:03:30.936 btags += '+' * lb
2025-07-01 03:03:30.941 elif tag == 'equal':
2025-07-01 03:03:30.946 atags += ' ' * la
2025-07-01 03:03:30.950 btags += ' ' * lb
2025-07-01 03:03:30.955 else:
2025-07-01 03:03:30.959 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:30.964 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:30.968 else:
2025-07-01 03:03:30.973 # the synch pair is identical
2025-07-01 03:03:30.977 yield '  ' + aelt
2025-07-01 03:03:30.982
2025-07-01 03:03:30.986 # pump out diffs from after the synch point
2025-07-01 03:03:30.991 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:30.995
2025-07-01 03:03:31.000 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:31.005 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:31.009
2025-07-01 03:03:31.013 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:31.019 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:31.023 alo = 322, ahi = 1101
2025-07-01 03:03:31.028 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:31.033 blo = 322, bhi = 1101
2025-07-01 03:03:31.037
2025-07-01 03:03:31.042 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:31.046 g = []
2025-07-01 03:03:31.050 if alo < ahi:
2025-07-01 03:03:31.055 if blo < bhi:
2025-07-01 03:03:31.059 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:31.063 else:
2025-07-01 03:03:31.068 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:31.072 elif blo < bhi:
2025-07-01 03:03:31.076 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:31.081
2025-07-01 03:03:31.087 >       yield from g
2025-07-01 03:03:31.091
2025-07-01 03:03:31.096 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:31.100 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:31.107
2025-07-01 03:03:31.111 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:31.124 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:31.139 alo = 322, ahi = 1101
2025-07-01 03:03:31.156 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:31.166 blo = 322, bhi = 1101
2025-07-01 03:03:31.172
2025-07-01 03:03:31.186 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:31.198 r"""
2025-07-01 03:03:31.210 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:31.217 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:31.230 synch point, and intraline difference marking is done on the
2025-07-01 03:03:31.238 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:31.250
2025-07-01 03:03:31.257 Example:
2025-07-01 03:03:31.270
2025-07-01 03:03:31.278 >>> d = Differ()
2025-07-01 03:03:31.294 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:31.302 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:31.310 >>> print(''.join(results), end="")
2025-07-01 03:03:31.322 - abcDefghiJkl
2025-07-01 03:03:31.341 + abcdefGhijkl
2025-07-01 03:03:31.362 """
2025-07-01 03:03:31.370
2025-07-01 03:03:31.382 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:31.394 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:31.407 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:31.422 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:31.434 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:31.450
2025-07-01 03:03:31.462 # search for the pair that matches best without being identical
2025-07-01 03:03:31.470 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:31.486 # on junk -- unless we have to)
2025-07-01 03:03:31.498 for j in range(blo, bhi):
2025-07-01 03:03:31.514 bj = b[j]
2025-07-01 03:03:31.526 cruncher.set_seq2(bj)
2025-07-01 03:03:31.538 for i in range(alo, ahi):
2025-07-01 03:03:31.550 ai = a[i]
2025-07-01 03:03:31.562 if ai == bj:
2025-07-01 03:03:31.570 if eqi is None:
2025-07-01 03:03:31.582 eqi, eqj = i, j
2025-07-01 03:03:31.594 continue
2025-07-01 03:03:31.602 cruncher.set_seq1(ai)
2025-07-01 03:03:31.614 # computing similarity is expensive, so use the quick
2025-07-01 03:03:31.626 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:31.638 # compares by a factor of 3.
2025-07-01 03:03:31.650 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:31.662 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:31.670 # of the computation is cached by cruncher
2025-07-01 03:03:31.682 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:31.690 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:31.702 cruncher.ratio() > best_ratio:
2025-07-01 03:03:31.716 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:31.726 if best_ratio < cutoff:
2025-07-01 03:03:31.738 # no non-identical "pretty close" pair
2025-07-01 03:03:31.746 if eqi is None:
2025-07-01 03:03:31.758 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:31.770 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:31.777 return
2025-07-01 03:03:31.793 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:31.801 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:31.814 else:
2025-07-01 03:03:31.828 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:31.841 eqi = None
2025-07-01 03:03:31.850
2025-07-01 03:03:31.859 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:31.874 # identical
2025-07-01 03:03:31.882
2025-07-01 03:03:31.898 # pump out diffs from before the synch point
2025-07-01 03:03:31.910 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:31.918
2025-07-01 03:03:31.931 # do intraline marking on the synch pair
2025-07-01 03:03:31.943 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:31.956 if eqi is None:
2025-07-01 03:03:31.966 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:31.978 atags = btags = ""
2025-07-01 03:03:31.990 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:32.000 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:32.011 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:32.020 if tag == 'replace':
2025-07-01 03:03:32.031 atags += '^' * la
2025-07-01 03:03:32.044 btags += '^' * lb
2025-07-01 03:03:32.056 elif tag == 'delete':
2025-07-01 03:03:32.066 atags += '-' * la
2025-07-01 03:03:32.074 elif tag == 'insert':
2025-07-01 03:03:32.086 btags += '+' * lb
2025-07-01 03:03:32.094 elif tag == 'equal':
2025-07-01 03:03:32.106 atags += ' ' * la
2025-07-01 03:03:32.113 btags += ' ' * lb
2025-07-01 03:03:32.129 else:
2025-07-01 03:03:32.142 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:32.155 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:32.173 else:
2025-07-01 03:03:32.180 # the synch pair is identical
2025-07-01 03:03:32.186 yield '  ' + aelt
2025-07-01 03:03:32.192
2025-07-01 03:03:32.199 # pump out diffs from after the synch point
2025-07-01 03:03:32.206 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:32.212
2025-07-01 03:03:32.219 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:32.226 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:32.233
2025-07-01 03:03:32.239 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:32.247 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:32.254 alo = 323, ahi = 1101
2025-07-01 03:03:32.262 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:32.267 blo = 323, bhi = 1101
2025-07-01 03:03:32.271
2025-07-01 03:03:32.276 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:32.280 g = []
2025-07-01 03:03:32.285 if alo < ahi:
2025-07-01 03:03:32.289 if blo < bhi:
2025-07-01 03:03:32.294 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:32.298 else:
2025-07-01 03:03:32.302 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:32.307 elif blo < bhi:
2025-07-01 03:03:32.311 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:32.316
2025-07-01 03:03:32.320 >       yield from g
2025-07-01 03:03:32.324
2025-07-01 03:03:32.328 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:32.333 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:32.337
2025-07-01 03:03:32.342 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:32.348 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:32.353 alo = 323, ahi = 1101
2025-07-01 03:03:32.360 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:32.365 blo = 323, bhi = 1101
2025-07-01 03:03:32.370
2025-07-01 03:03:32.374 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:32.379 r"""
2025-07-01 03:03:32.384 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:32.389 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:32.396 synch point, and intraline difference marking is done on the
2025-07-01 03:03:32.402 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:32.407
2025-07-01 03:03:32.411 Example:
2025-07-01 03:03:32.417
2025-07-01 03:03:32.422 >>> d = Differ()
2025-07-01 03:03:32.427 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:32.432 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:32.438 >>> print(''.join(results), end="")
2025-07-01 03:03:32.442 - abcDefghiJkl
2025-07-01 03:03:32.451 + abcdefGhijkl
2025-07-01 03:03:32.460 """
2025-07-01 03:03:32.464
2025-07-01 03:03:32.469 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:32.473 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:32.478 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:32.482 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:32.487 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:32.491
2025-07-01 03:03:32.496 # search for the pair that matches best without being identical
2025-07-01 03:03:32.500 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:32.505 # on junk -- unless we have to)
2025-07-01 03:03:32.510 for j in range(blo, bhi):
2025-07-01 03:03:32.514 bj = b[j]
2025-07-01 03:03:32.519 cruncher.set_seq2(bj)
2025-07-01 03:03:32.523 for i in range(alo, ahi):
2025-07-01 03:03:32.528 ai = a[i]
2025-07-01 03:03:32.532 if ai == bj:
2025-07-01 03:03:32.537 if eqi is None:
2025-07-01 03:03:32.541 eqi, eqj = i, j
2025-07-01 03:03:32.546 continue
2025-07-01 03:03:32.551 cruncher.set_seq1(ai)
2025-07-01 03:03:32.556 # computing similarity is expensive, so use the quick
2025-07-01 03:03:32.560 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:32.565 # compares by a factor of 3.
2025-07-01 03:03:32.570 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:32.575 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:32.579 # of the computation is cached by cruncher
2025-07-01 03:03:32.584 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:32.590 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:32.595 cruncher.ratio() > best_ratio:
2025-07-01 03:03:32.600 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:32.606 if best_ratio < cutoff:
2025-07-01 03:03:32.612 # no non-identical "pretty close" pair
2025-07-01 03:03:32.618 if eqi is None:
2025-07-01 03:03:32.623 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:32.628 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:32.632 return
2025-07-01 03:03:32.637 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:32.641 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:32.648 else:
2025-07-01 03:03:32.653 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:32.658 eqi = None
2025-07-01 03:03:32.662
2025-07-01 03:03:32.667 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:32.672 # identical
2025-07-01 03:03:32.676
2025-07-01 03:03:32.681 # pump out diffs from before the synch point
2025-07-01 03:03:32.685 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:32.690
2025-07-01 03:03:32.694 # do intraline marking on the synch pair
2025-07-01 03:03:32.699 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:32.704 if eqi is None:
2025-07-01 03:03:32.709 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:32.714 atags = btags = ""
2025-07-01 03:03:32.720 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:32.726 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:32.732 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:32.737 if tag == 'replace':
2025-07-01 03:03:32.742 atags += '^' * la
2025-07-01 03:03:32.748 btags += '^' * lb
2025-07-01 03:03:32.753 elif tag == 'delete':
2025-07-01 03:03:32.759 atags += '-' * la
2025-07-01 03:03:32.764 elif tag == 'insert':
2025-07-01 03:03:32.770 btags += '+' * lb
2025-07-01 03:03:32.775 elif tag == 'equal':
2025-07-01 03:03:32.779 atags += ' ' * la
2025-07-01 03:03:32.785 btags += ' ' * lb
2025-07-01 03:03:32.790 else:
2025-07-01 03:03:32.795 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:32.801 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:32.805 else:
2025-07-01 03:03:32.810 # the synch pair is identical
2025-07-01 03:03:32.816 yield '  ' + aelt
2025-07-01 03:03:32.821
2025-07-01 03:03:32.827 # pump out diffs from after the synch point
2025-07-01 03:03:32.832 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:32.837
2025-07-01 03:03:32.842 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:32.847 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:32.852
2025-07-01 03:03:32.857 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:32.863 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:32.868 alo = 324, ahi = 1101
2025-07-01 03:03:32.875 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:32.879 blo = 324, bhi = 1101
2025-07-01 03:03:32.884
2025-07-01 03:03:32.889 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:32.893 g = []
2025-07-01 03:03:32.899 if alo < ahi:
2025-07-01 03:03:32.904 if blo < bhi:
2025-07-01 03:03:32.910 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:32.915 else:
2025-07-01 03:03:32.921 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:32.926 elif blo < bhi:
2025-07-01 03:03:32.931 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:32.937
2025-07-01 03:03:32.942 >       yield from g
2025-07-01 03:03:32.948
2025-07-01 03:03:32.953 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:32.958 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:32.964
2025-07-01 03:03:32.970 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:32.975 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:32.980 alo = 324, ahi = 1101
2025-07-01 03:03:32.985 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:32.991 blo = 324, bhi = 1101
2025-07-01 03:03:32.996
2025-07-01 03:03:33.001 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:33.007 r"""
2025-07-01 03:03:33.012 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:33.017 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:33.021 synch point, and intraline difference marking is done on the
2025-07-01 03:03:33.026 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:33.031
2025-07-01 03:03:33.038 Example:
2025-07-01 03:03:33.043
2025-07-01 03:03:33.048 >>> d = Differ()
2025-07-01 03:03:33.052 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:33.060 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:33.071 >>> print(''.join(results), end="")
2025-07-01 03:03:33.086 - abcDefghiJkl
2025-07-01 03:03:33.109 + abcdefGhijkl
2025-07-01 03:03:33.137 """
2025-07-01 03:03:33.148
2025-07-01 03:03:33.158 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:33.177 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:33.190 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:33.202 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:33.213 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:33.226
2025-07-01 03:03:33.240 # search for the pair that matches best without being identical
2025-07-01 03:03:33.251 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:33.269 # on junk -- unless we have to)
2025-07-01 03:03:33.280 for j in range(blo, bhi):
2025-07-01 03:03:33.290 bj = b[j]
2025-07-01 03:03:33.301 cruncher.set_seq2(bj)
2025-07-01 03:03:33.313 for i in range(alo, ahi):
2025-07-01 03:03:33.322 ai = a[i]
2025-07-01 03:03:33.331 if ai == bj:
2025-07-01 03:03:33.342 if eqi is None:
2025-07-01 03:03:33.358 eqi, eqj = i, j
2025-07-01 03:03:33.365 continue
2025-07-01 03:03:33.375 cruncher.set_seq1(ai)
2025-07-01 03:03:33.385 # computing similarity is expensive, so use the quick
2025-07-01 03:03:33.401 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:33.415 # compares by a factor of 3.
2025-07-01 03:03:33.424 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:33.435 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:33.448 # of the computation is cached by cruncher
2025-07-01 03:03:33.462 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:33.474 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:33.486 cruncher.ratio() > best_ratio:
2025-07-01 03:03:33.498 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:33.510 if best_ratio < cutoff:
2025-07-01 03:03:33.518 # no non-identical "pretty close" pair
2025-07-01 03:03:33.530 if eqi is None:
2025-07-01 03:03:33.546 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:33.558 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:33.568 return
2025-07-01 03:03:33.576 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:33.594 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:33.602 else:
2025-07-01 03:03:33.618 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:33.630 eqi = None
2025-07-01 03:03:33.642
2025-07-01 03:03:33.658 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:33.668 # identical
2025-07-01 03:03:33.682
2025-07-01 03:03:33.698 # pump out diffs from before the synch point
2025-07-01 03:03:33.710 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:33.722
2025-07-01 03:03:33.737 # do intraline marking on the synch pair
2025-07-01 03:03:33.746 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:33.753 if eqi is None:
2025-07-01 03:03:33.766 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:33.778 atags = btags = ""
2025-07-01 03:03:33.786 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:33.799 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:33.814 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:33.823 if tag == 'replace':
2025-07-01 03:03:33.838 atags += '^' * la
2025-07-01 03:03:33.850 btags += '^' * lb
2025-07-01 03:03:33.858 elif tag == 'delete':
2025-07-01 03:03:33.870 atags += '-' * la
2025-07-01 03:03:33.886 elif tag == 'insert':
2025-07-01 03:03:33.892 btags += '+' * lb
2025-07-01 03:03:33.906 elif tag == 'equal':
2025-07-01 03:03:33.914 atags += ' ' * la
2025-07-01 03:03:33.926 btags += ' ' * lb
2025-07-01 03:03:33.936 else:
2025-07-01 03:03:33.954 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:33.962 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:33.974 else:
2025-07-01 03:03:33.996 # the synch pair is identical
2025-07-01 03:03:34.009 yield '  ' + aelt
2025-07-01 03:03:34.026
2025-07-01 03:03:34.034 # pump out diffs from after the synch point
2025-07-01 03:03:34.046 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:34.063
2025-07-01 03:03:34.081 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:34.102 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:34.115
2025-07-01 03:03:34.126 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:34.138 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:34.151 alo = 325, ahi = 1101
2025-07-01 03:03:34.165 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:34.186 blo = 325, bhi = 1101
2025-07-01 03:03:34.198
2025-07-01 03:03:34.210 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:34.217 g = []
2025-07-01 03:03:34.234 if alo < ahi:
2025-07-01 03:03:34.242 if blo < bhi:
2025-07-01 03:03:34.258 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:34.266 else:
2025-07-01 03:03:34.282 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:34.290 elif blo < bhi:
2025-07-01 03:03:34.306 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:34.314
2025-07-01 03:03:34.326 >       yield from g
2025-07-01 03:03:34.338
2025-07-01 03:03:34.346 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:34.358 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:34.370
2025-07-01 03:03:34.378 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:34.390 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:34.408 alo = 325, ahi = 1101
2025-07-01 03:03:34.421 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:34.434 blo = 325, bhi = 1101
2025-07-01 03:03:34.445
2025-07-01 03:03:34.455 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:34.468 r"""
2025-07-01 03:03:34.478 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:34.486 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:34.498 synch point, and intraline difference marking is done on the
2025-07-01 03:03:34.510 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:34.526
2025-07-01 03:03:34.538 Example:
2025-07-01 03:03:34.546
2025-07-01 03:03:34.558 >>> d = Differ()
2025-07-01 03:03:34.563 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:34.568 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:34.573 >>> print(''.join(results), end="")
2025-07-01 03:03:34.577 - abcDefghiJkl
2025-07-01 03:03:34.586 + abcdefGhijkl
2025-07-01 03:03:34.596 """
2025-07-01 03:03:34.601
2025-07-01 03:03:34.605 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:34.610 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:34.616 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:34.622 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:34.627 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:34.633
2025-07-01 03:03:34.638 # search for the pair that matches best without being identical
2025-07-01 03:03:34.644 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:34.648 # on junk -- unless we have to)
2025-07-01 03:03:34.653 for j in range(blo, bhi):
2025-07-01 03:03:34.659 bj = b[j]
2025-07-01 03:03:34.664 cruncher.set_seq2(bj)
2025-07-01 03:03:34.668 for i in range(alo, ahi):
2025-07-01 03:03:34.673 ai = a[i]
2025-07-01 03:03:34.678 if ai == bj:
2025-07-01 03:03:34.683 if eqi is None:
2025-07-01 03:03:34.687 eqi, eqj = i, j
2025-07-01 03:03:34.692 continue
2025-07-01 03:03:34.696 cruncher.set_seq1(ai)
2025-07-01 03:03:34.701 # computing similarity is expensive, so use the quick
2025-07-01 03:03:34.705 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:34.710 # compares by a factor of 3.
2025-07-01 03:03:34.715 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:34.719 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:34.724 # of the computation is cached by cruncher
2025-07-01 03:03:34.733 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:34.737 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:34.742 cruncher.ratio() > best_ratio:
2025-07-01 03:03:34.746 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:34.751 if best_ratio < cutoff:
2025-07-01 03:03:34.755 # no non-identical "pretty close" pair
2025-07-01 03:03:34.760 if eqi is None:
2025-07-01 03:03:34.764 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:34.769 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:34.773 return
2025-07-01 03:03:34.778 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:34.782 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:34.787 else:
2025-07-01 03:03:34.791 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:34.796 eqi = None
2025-07-01 03:03:34.800
2025-07-01 03:03:34.805 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:34.813 # identical
2025-07-01 03:03:34.819
2025-07-01 03:03:34.824 # pump out diffs from before the synch point
2025-07-01 03:03:34.828 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:34.833
2025-07-01 03:03:34.837 # do intraline marking on the synch pair
2025-07-01 03:03:34.842 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:34.847 if eqi is None:
2025-07-01 03:03:34.852 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:34.856 atags = btags = ""
2025-07-01 03:03:34.861 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:34.865 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:34.870 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:34.874 if tag == 'replace':
2025-07-01 03:03:34.879 atags += '^' * la
2025-07-01 03:03:34.883 btags += '^' * lb
2025-07-01 03:03:34.888 elif tag == 'delete':
2025-07-01 03:03:34.892 atags += '-' * la
2025-07-01 03:03:34.897 elif tag == 'insert':
2025-07-01 03:03:34.901 btags += '+' * lb
2025-07-01 03:03:34.906 elif tag == 'equal':
2025-07-01 03:03:34.911 atags += ' ' * la
2025-07-01 03:03:34.915 btags += ' ' * lb
2025-07-01 03:03:34.920 else:
2025-07-01 03:03:34.924 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:34.930 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:34.934 else:
2025-07-01 03:03:34.938 # the synch pair is identical
2025-07-01 03:03:34.944 yield '  ' + aelt
2025-07-01 03:03:34.948
2025-07-01 03:03:34.952 # pump out diffs from after the synch point
2025-07-01 03:03:34.957 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:34.962
2025-07-01 03:03:34.967 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:34.971 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:34.976
2025-07-01 03:03:34.981 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:34.986 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:34.990 alo = 326, ahi = 1101
2025-07-01 03:03:34.995 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:35.000 blo = 326, bhi = 1101
2025-07-01 03:03:35.004
2025-07-01 03:03:35.009 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:35.013 g = []
2025-07-01 03:03:35.018 if alo < ahi:
2025-07-01 03:03:35.022 if blo < bhi:
2025-07-01 03:03:35.027 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:35.032 else:
2025-07-01 03:03:35.037 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:35.041 elif blo < bhi:
2025-07-01 03:03:35.046 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:35.050
2025-07-01 03:03:35.055 >       yield from g
2025-07-01 03:03:35.059
2025-07-01 03:03:35.064 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:35.069 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:35.073
2025-07-01 03:03:35.077 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:35.083 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:35.087 alo = 326, ahi = 1101
2025-07-01 03:03:35.093 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:35.097 blo = 326, bhi = 1101
2025-07-01 03:03:35.101
2025-07-01 03:03:35.106 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:35.110 r"""
2025-07-01 03:03:35.115 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:35.120 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:35.125 synch point, and intraline difference marking is done on the
2025-07-01 03:03:35.129 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:35.134
2025-07-01 03:03:35.138 Example:
2025-07-01 03:03:35.143
2025-07-01 03:03:35.148 >>> d = Differ()
2025-07-01 03:03:35.152 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:35.157 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:35.162 >>> print(''.join(results), end="")
2025-07-01 03:03:35.166 - abcDefghiJkl
2025-07-01 03:03:35.175 + abcdefGhijkl
2025-07-01 03:03:35.184 """
2025-07-01 03:03:35.188
2025-07-01 03:03:35.192 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:35.197 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:35.202 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:35.206 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:35.211 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:35.215
2025-07-01 03:03:35.220 # search for the pair that matches best without being identical
2025-07-01 03:03:35.224 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:35.228 # on junk -- unless we have to)
2025-07-01 03:03:35.233 for j in range(blo, bhi):
2025-07-01 03:03:35.237 bj = b[j]
2025-07-01 03:03:35.242 cruncher.set_seq2(bj)
2025-07-01 03:03:35.250 for i in range(alo, ahi):
2025-07-01 03:03:35.254 ai = a[i]
2025-07-01 03:03:35.258 if ai == bj:
2025-07-01 03:03:35.263 if eqi is None:
2025-07-01 03:03:35.267 eqi, eqj = i, j
2025-07-01 03:03:35.271 continue
2025-07-01 03:03:35.276 cruncher.set_seq1(ai)
2025-07-01 03:03:35.282 # computing similarity is expensive, so use the quick
2025-07-01 03:03:35.287 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:35.291 # compares by a factor of 3.
2025-07-01 03:03:35.296 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:35.300 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:35.305 # of the computation is cached by cruncher
2025-07-01 03:03:35.310 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:35.314 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:35.319 cruncher.ratio() > best_ratio:
2025-07-01 03:03:35.323 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:35.328 if best_ratio < cutoff:
2025-07-01 03:03:35.332 # no non-identical "pretty close" pair
2025-07-01 03:03:35.336 if eqi is None:
2025-07-01 03:03:35.341 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:35.346 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:35.350 return
2025-07-01 03:03:35.357 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:35.363 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:35.369 else:
2025-07-01 03:03:35.375 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:35.381 eqi = None
2025-07-01 03:03:35.388
2025-07-01 03:03:35.394 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:35.400 # identical
2025-07-01 03:03:35.404
2025-07-01 03:03:35.410 # pump out diffs from before the synch point
2025-07-01 03:03:35.415 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:35.422
2025-07-01 03:03:35.428 # do intraline marking on the synch pair
2025-07-01 03:03:35.434 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:35.438 if eqi is None:
2025-07-01 03:03:35.443 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:35.447 atags = btags = ""
2025-07-01 03:03:35.453 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:35.458 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:35.462 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:35.466 if tag == 'replace':
2025-07-01 03:03:35.470 atags += '^' * la
2025-07-01 03:03:35.475 btags += '^' * lb
2025-07-01 03:03:35.479 elif tag == 'delete':
2025-07-01 03:03:35.483 atags += '-' * la
2025-07-01 03:03:35.487 elif tag == 'insert':
2025-07-01 03:03:35.492 btags += '+' * lb
2025-07-01 03:03:35.496 elif tag == 'equal':
2025-07-01 03:03:35.501 atags += ' ' * la
2025-07-01 03:03:35.505 btags += ' ' * lb
2025-07-01 03:03:35.509 else:
2025-07-01 03:03:35.514 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:35.519 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:35.524 else:
2025-07-01 03:03:35.529 # the synch pair is identical
2025-07-01 03:03:35.534 yield '  ' + aelt
2025-07-01 03:03:35.540
2025-07-01 03:03:35.547 # pump out diffs from after the synch point
2025-07-01 03:03:35.554 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:35.560
2025-07-01 03:03:35.566 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:35.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:35.578
2025-07-01 03:03:35.583 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:35.587 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:35.592 alo = 327, ahi = 1101
2025-07-01 03:03:35.597 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:35.603 blo = 327, bhi = 1101
2025-07-01 03:03:35.607
2025-07-01 03:03:35.611 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:35.616 g = []
2025-07-01 03:03:35.621 if alo < ahi:
2025-07-01 03:03:35.626 if blo < bhi:
2025-07-01 03:03:35.631 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:35.635 else:
2025-07-01 03:03:35.640 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:35.645 elif blo < bhi:
2025-07-01 03:03:35.650 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:35.654
2025-07-01 03:03:35.659 >       yield from g
2025-07-01 03:03:35.663
2025-07-01 03:03:35.668 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:35.673 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:35.678
2025-07-01 03:03:35.684 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:35.689 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:35.693 alo = 327, ahi = 1101
2025-07-01 03:03:35.699 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:35.704 blo = 327, bhi = 1101
2025-07-01 03:03:35.709
2025-07-01 03:03:35.714 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:35.719 r"""
2025-07-01 03:03:35.724 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:35.729 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:35.733 synch point, and intraline difference marking is done on the
2025-07-01 03:03:35.738 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:35.742
2025-07-01 03:03:35.747 Example:
2025-07-01 03:03:35.751
2025-07-01 03:03:35.756 >>> d = Differ()
2025-07-01 03:03:35.760 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:35.765 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:35.770 >>> print(''.join(results), end="")
2025-07-01 03:03:35.774 - abcDefghiJkl
2025-07-01 03:03:35.786 + abcdefGhijkl
2025-07-01 03:03:35.795 """
2025-07-01 03:03:35.803
2025-07-01 03:03:35.808 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:35.812 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:35.818 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:35.822 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:35.827 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:35.831
2025-07-01 03:03:35.836 # search for the pair that matches best without being identical
2025-07-01 03:03:35.841 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:35.845 # on junk -- unless we have to)
2025-07-01 03:03:35.850 for j in range(blo, bhi):
2025-07-01 03:03:35.856 bj = b[j]
2025-07-01 03:03:35.861 cruncher.set_seq2(bj)
2025-07-01 03:03:35.865 for i in range(alo, ahi):
2025-07-01 03:03:35.870 ai = a[i]
2025-07-01 03:03:35.876 if ai == bj:
2025-07-01 03:03:35.881 if eqi is None:
2025-07-01 03:03:35.885 eqi, eqj = i, j
2025-07-01 03:03:35.890 continue
2025-07-01 03:03:35.895 cruncher.set_seq1(ai)
2025-07-01 03:03:35.899 # computing similarity is expensive, so use the quick
2025-07-01 03:03:35.904 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:35.908 # compares by a factor of 3.
2025-07-01 03:03:35.913 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:35.917 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:35.922 # of the computation is cached by cruncher
2025-07-01 03:03:35.926 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:35.931 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:35.936 cruncher.ratio() > best_ratio:
2025-07-01 03:03:35.942 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:35.948 if best_ratio < cutoff:
2025-07-01 03:03:35.954 # no non-identical "pretty close" pair
2025-07-01 03:03:35.961 if eqi is None:
2025-07-01 03:03:35.967 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:35.971 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:35.977 return
2025-07-01 03:03:35.982 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:35.987 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:35.991 else:
2025-07-01 03:03:35.996 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:36.001 eqi = None
2025-07-01 03:03:36.007
2025-07-01 03:03:36.012 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:36.017 # identical
2025-07-01 03:03:36.022
2025-07-01 03:03:36.027 # pump out diffs from before the synch point
2025-07-01 03:03:36.032 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:36.036
2025-07-01 03:03:36.041 # do intraline marking on the synch pair
2025-07-01 03:03:36.046 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:36.050 if eqi is None:
2025-07-01 03:03:36.055 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:36.060 atags = btags = ""
2025-07-01 03:03:36.064 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:36.069 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:36.074 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:36.078 if tag == 'replace':
2025-07-01 03:03:36.083 atags += '^' * la
2025-07-01 03:03:36.089 btags += '^' * lb
2025-07-01 03:03:36.095 elif tag == 'delete':
2025-07-01 03:03:36.101 atags += '-' * la
2025-07-01 03:03:36.107 elif tag == 'insert':
2025-07-01 03:03:36.113 btags += '+' * lb
2025-07-01 03:03:36.119 elif tag == 'equal':
2025-07-01 03:03:36.125 atags += ' ' * la
2025-07-01 03:03:36.131 btags += ' ' * lb
2025-07-01 03:03:36.137 else:
2025-07-01 03:03:36.143 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:36.149 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:36.155 else:
2025-07-01 03:03:36.161 # the synch pair is identical
2025-07-01 03:03:36.167 yield '  ' + aelt
2025-07-01 03:03:36.173
2025-07-01 03:03:36.179 # pump out diffs from after the synch point
2025-07-01 03:03:36.185 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:36.191
2025-07-01 03:03:36.197 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:36.203 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:36.209
2025-07-01 03:03:36.215 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:36.225 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:36.231 alo = 328, ahi = 1101
2025-07-01 03:03:36.237 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:36.243 blo = 328, bhi = 1101
2025-07-01 03:03:36.248
2025-07-01 03:03:36.252 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:36.257 g = []
2025-07-01 03:03:36.261 if alo < ahi:
2025-07-01 03:03:36.265 if blo < bhi:
2025-07-01 03:03:36.270 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:36.274 else:
2025-07-01 03:03:36.278 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:36.283 elif blo < bhi:
2025-07-01 03:03:36.287 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:36.292
2025-07-01 03:03:36.299 >       yield from g
2025-07-01 03:03:36.304
2025-07-01 03:03:36.308 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:36.313 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:36.317
2025-07-01 03:03:36.324 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:36.329 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:36.334 alo = 328, ahi = 1101
2025-07-01 03:03:36.339 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:36.343 blo = 328, bhi = 1101
2025-07-01 03:03:36.348
2025-07-01 03:03:36.353 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:36.357 r"""
2025-07-01 03:03:36.362 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:36.367 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:36.372 synch point, and intraline difference marking is done on the
2025-07-01 03:03:36.376 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:36.381
2025-07-01 03:03:36.386 Example:
2025-07-01 03:03:36.391
2025-07-01 03:03:36.395 >>> d = Differ()
2025-07-01 03:03:36.400 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:36.405 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:36.411 >>> print(''.join(results), end="")
2025-07-01 03:03:36.415 - abcDefghiJkl
2025-07-01 03:03:36.424 + abcdefGhijkl
2025-07-01 03:03:36.434 """
2025-07-01 03:03:36.438
2025-07-01 03:03:36.443 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:36.447 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:36.452 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:36.456 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:36.461 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:36.466
2025-07-01 03:03:36.470 # search for the pair that matches best without being identical
2025-07-01 03:03:36.475 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:36.479 # on junk -- unless we have to)
2025-07-01 03:03:36.484 for j in range(blo, bhi):
2025-07-01 03:03:36.488 bj = b[j]
2025-07-01 03:03:36.493 cruncher.set_seq2(bj)
2025-07-01 03:03:36.498 for i in range(alo, ahi):
2025-07-01 03:03:36.503 ai = a[i]
2025-07-01 03:03:36.507 if ai == bj:
2025-07-01 03:03:36.512 if eqi is None:
2025-07-01 03:03:36.517 eqi, eqj = i, j
2025-07-01 03:03:36.522 continue
2025-07-01 03:03:36.526 cruncher.set_seq1(ai)
2025-07-01 03:03:36.531 # computing similarity is expensive, so use the quick
2025-07-01 03:03:36.536 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:36.541 # compares by a factor of 3.
2025-07-01 03:03:36.546 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:36.551 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:36.557 # of the computation is cached by cruncher
2025-07-01 03:03:36.562 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:36.567 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:36.572 cruncher.ratio() > best_ratio:
2025-07-01 03:03:36.577 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:36.582 if best_ratio < cutoff:
2025-07-01 03:03:36.586 # no non-identical "pretty close" pair
2025-07-01 03:03:36.591 if eqi is None:
2025-07-01 03:03:36.595 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:36.601 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:36.607 return
2025-07-01 03:03:36.614 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:36.620 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:36.626 else:
2025-07-01 03:03:36.631 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:36.636 eqi = None
2025-07-01 03:03:36.641
2025-07-01 03:03:36.645 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:36.650 # identical
2025-07-01 03:03:36.655
2025-07-01 03:03:36.659 # pump out diffs from before the synch point
2025-07-01 03:03:36.664 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:36.668
2025-07-01 03:03:36.672 # do intraline marking on the synch pair
2025-07-01 03:03:36.677 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:36.681 if eqi is None:
2025-07-01 03:03:36.686 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:36.691 atags = btags = ""
2025-07-01 03:03:36.696 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:36.701 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:36.705 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:36.710 if tag == 'replace':
2025-07-01 03:03:36.714 atags += '^' * la
2025-07-01 03:03:36.719 btags += '^' * lb
2025-07-01 03:03:36.723 elif tag == 'delete':
2025-07-01 03:03:36.728 atags += '-' * la
2025-07-01 03:03:36.733 elif tag == 'insert':
2025-07-01 03:03:36.738 btags += '+' * lb
2025-07-01 03:03:36.743 elif tag == 'equal':
2025-07-01 03:03:36.748 atags += ' ' * la
2025-07-01 03:03:36.753 btags += ' ' * lb
2025-07-01 03:03:36.758 else:
2025-07-01 03:03:36.763 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:36.768 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:36.773 else:
2025-07-01 03:03:36.778 # the synch pair is identical
2025-07-01 03:03:36.783 yield '  ' + aelt
2025-07-01 03:03:36.787
2025-07-01 03:03:36.792 # pump out diffs from after the synch point
2025-07-01 03:03:36.796 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:36.801
2025-07-01 03:03:36.806 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:36.811 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:36.815
2025-07-01 03:03:36.820 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:36.824 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:36.829 alo = 329, ahi = 1101
2025-07-01 03:03:36.834 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:36.838 blo = 329, bhi = 1101
2025-07-01 03:03:36.843
2025-07-01 03:03:36.848 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:36.852 g = []
2025-07-01 03:03:36.857 if alo < ahi:
2025-07-01 03:03:36.862 if blo < bhi:
2025-07-01 03:03:36.869 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:36.874 else:
2025-07-01 03:03:36.879 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:36.883 elif blo < bhi:
2025-07-01 03:03:36.888 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:36.893
2025-07-01 03:03:36.898 >       yield from g
2025-07-01 03:03:36.902
2025-07-01 03:03:36.906 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:36.911 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:36.915
2025-07-01 03:03:36.920 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:36.925 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:36.930 alo = 329, ahi = 1101
2025-07-01 03:03:36.935 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:36.940 blo = 329, bhi = 1101
2025-07-01 03:03:36.944
2025-07-01 03:03:36.949 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:36.953 r"""
2025-07-01 03:03:36.958 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:36.963 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:36.967 synch point, and intraline difference marking is done on the
2025-07-01 03:03:36.972 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:36.977
2025-07-01 03:03:36.982 Example:
2025-07-01 03:03:36.986
2025-07-01 03:03:36.991 >>> d = Differ()
2025-07-01 03:03:36.995 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:37.000 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:37.005 >>> print(''.join(results), end="")
2025-07-01 03:03:37.012 - abcDefghiJkl
2025-07-01 03:03:37.025 + abcdefGhijkl
2025-07-01 03:03:37.039 """
2025-07-01 03:03:37.046
2025-07-01 03:03:37.051 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:37.056 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:37.061 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:37.067 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:37.071 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:37.076
2025-07-01 03:03:37.080 # search for the pair that matches best without being identical
2025-07-01 03:03:37.085 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:37.089 # on junk -- unless we have to)
2025-07-01 03:03:37.094 for j in range(blo, bhi):
2025-07-01 03:03:37.098 bj = b[j]
2025-07-01 03:03:37.103 cruncher.set_seq2(bj)
2025-07-01 03:03:37.107 for i in range(alo, ahi):
2025-07-01 03:03:37.111 ai = a[i]
2025-07-01 03:03:37.116 if ai == bj:
2025-07-01 03:03:37.120 if eqi is None:
2025-07-01 03:03:37.125 eqi, eqj = i, j
2025-07-01 03:03:37.129 continue
2025-07-01 03:03:37.133 cruncher.set_seq1(ai)
2025-07-01 03:03:37.138 # computing similarity is expensive, so use the quick
2025-07-01 03:03:37.143 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:37.147 # compares by a factor of 3.
2025-07-01 03:03:37.152 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:37.156 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:37.161 # of the computation is cached by cruncher
2025-07-01 03:03:37.166 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:37.171 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:37.175 cruncher.ratio() > best_ratio:
2025-07-01 03:03:37.180 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:37.184 if best_ratio < cutoff:
2025-07-01 03:03:37.189 # no non-identical "pretty close" pair
2025-07-01 03:03:37.193 if eqi is None:
2025-07-01 03:03:37.198 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:37.202 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:37.206 return
2025-07-01 03:03:37.211 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:37.215 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:37.220 else:
2025-07-01 03:03:37.224 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:37.229 eqi = None
2025-07-01 03:03:37.233
2025-07-01 03:03:37.238 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:37.242 # identical
2025-07-01 03:03:37.246
2025-07-01 03:03:37.251 # pump out diffs from before the synch point
2025-07-01 03:03:37.255 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:37.260
2025-07-01 03:03:37.264 # do intraline marking on the synch pair
2025-07-01 03:03:37.269 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:37.273 if eqi is None:
2025-07-01 03:03:37.278 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:37.282 atags = btags = ""
2025-07-01 03:03:37.287 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:37.291 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:37.296 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:37.300 if tag == 'replace':
2025-07-01 03:03:37.304 atags += '^' * la
2025-07-01 03:03:37.309 btags += '^' * lb
2025-07-01 03:03:37.313 elif tag == 'delete':
2025-07-01 03:03:37.317 atags += '-' * la
2025-07-01 03:03:37.322 elif tag == 'insert':
2025-07-01 03:03:37.326 btags += '+' * lb
2025-07-01 03:03:37.331 elif tag == 'equal':
2025-07-01 03:03:37.335 atags += ' ' * la
2025-07-01 03:03:37.340 btags += ' ' * lb
2025-07-01 03:03:37.344 else:
2025-07-01 03:03:37.349 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:37.354 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:37.358 else:
2025-07-01 03:03:37.362 # the synch pair is identical
2025-07-01 03:03:37.369 yield '  ' + aelt
2025-07-01 03:03:37.373
2025-07-01 03:03:37.378 # pump out diffs from after the synch point
2025-07-01 03:03:37.382 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:37.387
2025-07-01 03:03:37.392 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:37.396 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:37.401
2025-07-01 03:03:37.407 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:37.411 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:37.416 alo = 330, ahi = 1101
2025-07-01 03:03:37.421 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:37.425 blo = 330, bhi = 1101
2025-07-01 03:03:37.430
2025-07-01 03:03:37.435 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:37.439 g = []
2025-07-01 03:03:37.444 if alo < ahi:
2025-07-01 03:03:37.449 if blo < bhi:
2025-07-01 03:03:37.453 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:37.458 else:
2025-07-01 03:03:37.463 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:37.467 elif blo < bhi:
2025-07-01 03:03:37.472 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:37.476
2025-07-01 03:03:37.481 >       yield from g
2025-07-01 03:03:37.485
2025-07-01 03:03:37.490 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:37.494 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:37.498
2025-07-01 03:03:37.503 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:37.507 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:37.512 alo = 330, ahi = 1101
2025-07-01 03:03:37.517 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:37.522 blo = 330, bhi = 1101
2025-07-01 03:03:37.527
2025-07-01 03:03:37.532 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:37.537 r"""
2025-07-01 03:03:37.541 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:37.546 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:37.550 synch point, and intraline difference marking is done on the
2025-07-01 03:03:37.555 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:37.560
2025-07-01 03:03:37.564 Example:
2025-07-01 03:03:37.568
2025-07-01 03:03:37.573 >>> d = Differ()
2025-07-01 03:03:37.577 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:37.581 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:37.586 >>> print(''.join(results), end="")
2025-07-01 03:03:37.590 - abcDefghiJkl
2025-07-01 03:03:37.599 + abcdefGhijkl
2025-07-01 03:03:37.634 """
2025-07-01 03:03:37.656
2025-07-01 03:03:37.661 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:37.665 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:37.670 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:37.674 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:37.679 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:37.684
2025-07-01 03:03:37.688 # search for the pair that matches best without being identical
2025-07-01 03:03:37.693 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:37.698 # on junk -- unless we have to)
2025-07-01 03:03:37.703 for j in range(blo, bhi):
2025-07-01 03:03:37.708 bj = b[j]
2025-07-01 03:03:37.712 cruncher.set_seq2(bj)
2025-07-01 03:03:37.717 for i in range(alo, ahi):
2025-07-01 03:03:37.721 ai = a[i]
2025-07-01 03:03:37.726 if ai == bj:
2025-07-01 03:03:37.730 if eqi is None:
2025-07-01 03:03:37.735 eqi, eqj = i, j
2025-07-01 03:03:37.740 continue
2025-07-01 03:03:37.744 cruncher.set_seq1(ai)
2025-07-01 03:03:37.749 # computing similarity is expensive, so use the quick
2025-07-01 03:03:37.754 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:37.759 # compares by a factor of 3.
2025-07-01 03:03:37.763 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:37.769 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:37.774 # of the computation is cached by cruncher
2025-07-01 03:03:37.779 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:37.784 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:37.789 cruncher.ratio() > best_ratio:
2025-07-01 03:03:37.794 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:37.799 if best_ratio < cutoff:
2025-07-01 03:03:37.803 # no non-identical "pretty close" pair
2025-07-01 03:03:37.808 if eqi is None:
2025-07-01 03:03:37.813 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:37.818 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:37.823 return
2025-07-01 03:03:37.828 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:37.832 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:37.837 else:
2025-07-01 03:03:37.842 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:37.846 eqi = None
2025-07-01 03:03:37.851
2025-07-01 03:03:37.857 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:37.862 # identical
2025-07-01 03:03:37.883
2025-07-01 03:03:37.890 # pump out diffs from before the synch point
2025-07-01 03:03:37.902 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:37.915
2025-07-01 03:03:37.923 # do intraline marking on the synch pair
2025-07-01 03:03:37.939 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:37.966 if eqi is None:
2025-07-01 03:03:37.985 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:38.001 atags = btags = ""
2025-07-01 03:03:38.017 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:38.033 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:38.050 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:38.059 if tag == 'replace':
2025-07-01 03:03:38.074 atags += '^' * la
2025-07-01 03:03:38.086 btags += '^' * lb
2025-07-01 03:03:38.098 elif tag == 'delete':
2025-07-01 03:03:38.110 atags += '-' * la
2025-07-01 03:03:38.122 elif tag == 'insert':
2025-07-01 03:03:38.139 btags += '+' * lb
2025-07-01 03:03:38.146 elif tag == 'equal':
2025-07-01 03:03:38.157 atags += ' ' * la
2025-07-01 03:03:38.174 btags += ' ' * lb
2025-07-01 03:03:38.190 else:
2025-07-01 03:03:38.202 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:38.214 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:38.222 else:
2025-07-01 03:03:38.234 # the synch pair is identical
2025-07-01 03:03:38.242 yield '  ' + aelt
2025-07-01 03:03:38.254
2025-07-01 03:03:38.268 # pump out diffs from after the synch point
2025-07-01 03:03:38.278 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:38.288
2025-07-01 03:03:38.302 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:38.312 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:38.326
2025-07-01 03:03:38.336 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:38.354 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:38.362 alo = 331, ahi = 1101
2025-07-01 03:03:38.378 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:38.391 blo = 331, bhi = 1101
2025-07-01 03:03:38.406
2025-07-01 03:03:38.424 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:38.434 g = []
2025-07-01 03:03:38.450 if alo < ahi:
2025-07-01 03:03:38.457 if blo < bhi:
2025-07-01 03:03:38.470 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:38.482 else:
2025-07-01 03:03:38.494 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:38.505 elif blo < bhi:
2025-07-01 03:03:38.514 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:38.526
2025-07-01 03:03:38.535 >       yield from g
2025-07-01 03:03:38.550
2025-07-01 03:03:38.558 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:38.573 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:38.582
2025-07-01 03:03:38.593 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:38.610 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:38.618 alo = 331, ahi = 1101
2025-07-01 03:03:38.634 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:38.642 blo = 331, bhi = 1101
2025-07-01 03:03:38.660
2025-07-01 03:03:38.673 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:38.683 r"""
2025-07-01 03:03:38.700 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:38.714 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:38.721 synch point, and intraline difference marking is done on the
2025-07-01 03:03:38.734 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:38.750
2025-07-01 03:03:38.758 Example:
2025-07-01 03:03:38.770
2025-07-01 03:03:38.790 >>> d = Differ()
2025-07-01 03:03:38.797 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:38.809 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:38.822 >>> print(''.join(results), end="")
2025-07-01 03:03:38.834 - abcDefghiJkl
2025-07-01 03:03:38.854 + abcdefGhijkl
2025-07-01 03:03:38.870 """
2025-07-01 03:03:38.882
2025-07-01 03:03:38.890 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:38.906 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:38.914 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:38.926 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:38.938 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:38.946
2025-07-01 03:03:38.959 # search for the pair that matches best without being identical
2025-07-01 03:03:38.974 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:38.986 # on junk -- unless we have to)
2025-07-01 03:03:38.997 for j in range(blo, bhi):
2025-07-01 03:03:39.006 bj = b[j]
2025-07-01 03:03:39.016 cruncher.set_seq2(bj)
2025-07-01 03:03:39.030 for i in range(alo, ahi):
2025-07-01 03:03:39.042 ai = a[i]
2025-07-01 03:03:39.053 if ai == bj:
2025-07-01 03:03:39.062 if eqi is None:
2025-07-01 03:03:39.081 eqi, eqj = i, j
2025-07-01 03:03:39.090 continue
2025-07-01 03:03:39.106 cruncher.set_seq1(ai)
2025-07-01 03:03:39.116 # computing similarity is expensive, so use the quick
2025-07-01 03:03:39.126 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:39.140 # compares by a factor of 3.
2025-07-01 03:03:39.150 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:39.158 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:39.170 # of the computation is cached by cruncher
2025-07-01 03:03:39.180 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:39.190 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:39.202 cruncher.ratio() > best_ratio:
2025-07-01 03:03:39.212 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:39.222 if best_ratio < cutoff:
2025-07-01 03:03:39.237 # no non-identical "pretty close" pair
2025-07-01 03:03:39.244 if eqi is None:
2025-07-01 03:03:39.256 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:39.270 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:39.277 return
2025-07-01 03:03:39.294 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:39.310 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:39.318 else:
2025-07-01 03:03:39.330 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:39.338 eqi = None
2025-07-01 03:03:39.350
2025-07-01 03:03:39.361 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:39.370 # identical
2025-07-01 03:03:39.377
2025-07-01 03:03:39.390 # pump out diffs from before the synch point
2025-07-01 03:03:39.398 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:39.410
2025-07-01 03:03:39.422 # do intraline marking on the synch pair
2025-07-01 03:03:39.430 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:39.442 if eqi is None:
2025-07-01 03:03:39.454 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:39.466 atags = btags = ""
2025-07-01 03:03:39.478 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:39.490 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:39.498 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:39.510 if tag == 'replace':
2025-07-01 03:03:39.518 atags += '^' * la
2025-07-01 03:03:39.530 btags += '^' * lb
2025-07-01 03:03:39.538 elif tag == 'delete':
2025-07-01 03:03:39.550 atags += '-' * la
2025-07-01 03:03:39.558 elif tag == 'insert':
2025-07-01 03:03:39.573 btags += '+' * lb
2025-07-01 03:03:39.584 elif tag == 'equal':
2025-07-01 03:03:39.592 atags += ' ' * la
2025-07-01 03:03:39.608 btags += ' ' * lb
2025-07-01 03:03:39.618 else:
2025-07-01 03:03:39.634 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:39.642 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:39.660 else:
2025-07-01 03:03:39.674 # the synch pair is identical
2025-07-01 03:03:39.682 yield '  ' + aelt
2025-07-01 03:03:39.698
2025-07-01 03:03:39.714 # pump out diffs from after the synch point
2025-07-01 03:03:39.730 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:39.746
2025-07-01 03:03:39.760 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:39.778 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:39.790
2025-07-01 03:03:39.802 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:39.818 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:39.826 alo = 334, ahi = 1101
2025-07-01 03:03:39.838 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:39.850 blo = 334, bhi = 1101
2025-07-01 03:03:39.858
2025-07-01 03:03:39.870 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:39.878 g = []
2025-07-01 03:03:39.890 if alo < ahi:
2025-07-01 03:03:39.903 if blo < bhi:
2025-07-01 03:03:39.914 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:39.930 else:
2025-07-01 03:03:39.942 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:39.958 elif blo < bhi:
2025-07-01 03:03:39.974 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:39.994
2025-07-01 03:03:40.006 >       yield from g
2025-07-01 03:03:40.018
2025-07-01 03:03:40.026 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:40.038 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:40.054
2025-07-01 03:03:40.062 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:40.082 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:40.094 alo = 334, ahi = 1101
2025-07-01 03:03:40.110 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:40.126 blo = 334, bhi = 1101
2025-07-01 03:03:40.137
2025-07-01 03:03:40.149 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:40.162 r"""
2025-07-01 03:03:40.174 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:40.189 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:40.202 synch point, and intraline difference marking is done on the
2025-07-01 03:03:40.210 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:40.221
2025-07-01 03:03:40.230 Example:
2025-07-01 03:03:40.242
2025-07-01 03:03:40.253 >>> d = Differ()
2025-07-01 03:03:40.261 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:40.274 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:40.286 >>> print(''.join(results), end="")
2025-07-01 03:03:40.297 - abcDefghiJkl
2025-07-01 03:03:40.326 + abcdefGhijkl
2025-07-01 03:03:40.352 """
2025-07-01 03:03:40.366
2025-07-01 03:03:40.374 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:40.382 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:40.394 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:40.410 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:40.420 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:40.438
2025-07-01 03:03:40.447 # search for the pair that matches best without being identical
2025-07-01 03:03:40.462 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:40.470 # on junk -- unless we have to)
2025-07-01 03:03:40.482 for j in range(blo, bhi):
2025-07-01 03:03:40.490 bj = b[j]
2025-07-01 03:03:40.502 cruncher.set_seq2(bj)
2025-07-01 03:03:40.518 for i in range(alo, ahi):
2025-07-01 03:03:40.526 ai = a[i]
2025-07-01 03:03:40.534 if ai == bj:
2025-07-01 03:03:40.546 if eqi is None:
2025-07-01 03:03:40.558 eqi, eqj = i, j
2025-07-01 03:03:40.569 continue
2025-07-01 03:03:40.578 cruncher.set_seq1(ai)
2025-07-01 03:03:40.590 # computing similarity is expensive, so use the quick
2025-07-01 03:03:40.602 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:40.613 # compares by a factor of 3.
2025-07-01 03:03:40.627 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:40.641 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:40.650 # of the computation is cached by cruncher
2025-07-01 03:03:40.658 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:40.665 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:40.675 cruncher.ratio() > best_ratio:
2025-07-01 03:03:40.694 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:40.709 if best_ratio < cutoff:
2025-07-01 03:03:40.717 # no non-identical "pretty close" pair
2025-07-01 03:03:40.729 if eqi is None:
2025-07-01 03:03:40.742 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:40.754 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:40.763 return
2025-07-01 03:03:40.774 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:40.779 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:40.783 else:
2025-07-01 03:03:40.788 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:40.792 eqi = None
2025-07-01 03:03:40.797
2025-07-01 03:03:40.801 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:40.808 # identical
2025-07-01 03:03:40.812
2025-07-01 03:03:40.817 # pump out diffs from before the synch point
2025-07-01 03:03:40.822 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:40.826
2025-07-01 03:03:40.830 # do intraline marking on the synch pair
2025-07-01 03:03:40.835 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:40.839 if eqi is None:
2025-07-01 03:03:40.844 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:40.849 atags = btags = ""
2025-07-01 03:03:40.854 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:40.862 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:40.867 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:40.872 if tag == 'replace':
2025-07-01 03:03:40.877 atags += '^' * la
2025-07-01 03:03:40.882 btags += '^' * lb
2025-07-01 03:03:40.888 elif tag == 'delete':
2025-07-01 03:03:40.896 atags += '-' * la
2025-07-01 03:03:40.911 elif tag == 'insert':
2025-07-01 03:03:40.919 btags += '+' * lb
2025-07-01 03:03:40.925 elif tag == 'equal':
2025-07-01 03:03:40.930 atags += ' ' * la
2025-07-01 03:03:40.935 btags += ' ' * lb
2025-07-01 03:03:40.939 else:
2025-07-01 03:03:40.944 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:40.948 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:40.953 else:
2025-07-01 03:03:40.958 # the synch pair is identical
2025-07-01 03:03:40.962 yield '  ' + aelt
2025-07-01 03:03:40.967
2025-07-01 03:03:40.971 # pump out diffs from after the synch point
2025-07-01 03:03:40.976 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:40.981
2025-07-01 03:03:40.985 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:40.990 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:40.994
2025-07-01 03:03:40.999 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:41.004 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:41.009 alo = 335, ahi = 1101
2025-07-01 03:03:41.014 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:41.019 blo = 335, bhi = 1101
2025-07-01 03:03:41.023
2025-07-01 03:03:41.027 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:41.032 g = []
2025-07-01 03:03:41.036 if alo < ahi:
2025-07-01 03:03:41.041 if blo < bhi:
2025-07-01 03:03:41.045 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:41.050 else:
2025-07-01 03:03:41.054 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:41.059 elif blo < bhi:
2025-07-01 03:03:41.063 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:41.068
2025-07-01 03:03:41.072 >       yield from g
2025-07-01 03:03:41.077
2025-07-01 03:03:41.088 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:41.106 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:41.113
2025-07-01 03:03:41.128 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:41.142 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:41.154 alo = 335, ahi = 1101
2025-07-01 03:03:41.170 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:41.182 blo = 335, bhi = 1101
2025-07-01 03:03:41.190
2025-07-01 03:03:41.206 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:41.218 r"""
2025-07-01 03:03:41.230 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:41.238 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:41.250 synch point, and intraline difference marking is done on the
2025-07-01 03:03:41.270 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:41.278
2025-07-01 03:03:41.290 Example:
2025-07-01 03:03:41.300
2025-07-01 03:03:41.314 >>> d = Differ()
2025-07-01 03:03:41.325 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:41.338 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:41.350 >>> print(''.join(results), end="")
2025-07-01 03:03:41.358 - abcDefghiJkl
2025-07-01 03:03:41.382 + abcdefGhijkl
2025-07-01 03:03:41.398 """
2025-07-01 03:03:41.410
2025-07-01 03:03:41.420 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:41.436 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:41.448 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:41.462 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:41.474 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:41.484
2025-07-01 03:03:41.494 # search for the pair that matches best without being identical
2025-07-01 03:03:41.506 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:41.514 # on junk -- unless we have to)
2025-07-01 03:03:41.525 for j in range(blo, bhi):
2025-07-01 03:03:41.533 bj = b[j]
2025-07-01 03:03:41.546 cruncher.set_seq2(bj)
2025-07-01 03:03:41.554 for i in range(alo, ahi):
2025-07-01 03:03:41.565 ai = a[i]
2025-07-01 03:03:41.574 if ai == bj:
2025-07-01 03:03:41.586 if eqi is None:
2025-07-01 03:03:41.596 eqi, eqj = i, j
2025-07-01 03:03:41.614 continue
2025-07-01 03:03:41.622 cruncher.set_seq1(ai)
2025-07-01 03:03:41.633 # computing similarity is expensive, so use the quick
2025-07-01 03:03:41.646 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:41.658 # compares by a factor of 3.
2025-07-01 03:03:41.665 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:41.678 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:41.690 # of the computation is cached by cruncher
2025-07-01 03:03:41.700 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:41.714 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:41.726 cruncher.ratio() > best_ratio:
2025-07-01 03:03:41.734 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:41.748 if best_ratio < cutoff:
2025-07-01 03:03:41.758 # no non-identical "pretty close" pair
2025-07-01 03:03:41.772 if eqi is None:
2025-07-01 03:03:41.782 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:41.794 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:41.802 return
2025-07-01 03:03:41.814 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:41.826 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:41.834 else:
2025-07-01 03:03:41.846 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:41.854 eqi = None
2025-07-01 03:03:41.866
2025-07-01 03:03:41.874 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:41.890 # identical
2025-07-01 03:03:41.898
2025-07-01 03:03:41.906 # pump out diffs from before the synch point
2025-07-01 03:03:41.918 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:41.928
2025-07-01 03:03:41.938 # do intraline marking on the synch pair
2025-07-01 03:03:41.950 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:41.962 if eqi is None:
2025-07-01 03:03:41.970 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:41.982 atags = btags = ""
2025-07-01 03:03:41.990 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:42.009 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:42.018 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:42.033 if tag == 'replace':
2025-07-01 03:03:42.045 atags += '^' * la
2025-07-01 03:03:42.051 btags += '^' * lb
2025-07-01 03:03:42.057 elif tag == 'delete':
2025-07-01 03:03:42.068 atags += '-' * la
2025-07-01 03:03:42.080 elif tag == 'insert':
2025-07-01 03:03:42.088 btags += '+' * lb
2025-07-01 03:03:42.094 elif tag == 'equal':
2025-07-01 03:03:42.099 atags += ' ' * la
2025-07-01 03:03:42.105 btags += ' ' * lb
2025-07-01 03:03:42.111 else:
2025-07-01 03:03:42.117 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:42.122 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:42.128 else:
2025-07-01 03:03:42.134 # the synch pair is identical
2025-07-01 03:03:42.138 yield '  ' + aelt
2025-07-01 03:03:42.144
2025-07-01 03:03:42.150 # pump out diffs from after the synch point
2025-07-01 03:03:42.155 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:42.160
2025-07-01 03:03:42.166 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:42.171 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:42.177
2025-07-01 03:03:42.182 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:42.189 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:42.195 alo = 336, ahi = 1101
2025-07-01 03:03:42.202 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:42.208 blo = 336, bhi = 1101
2025-07-01 03:03:42.212
2025-07-01 03:03:42.218 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:42.223 g = []
2025-07-01 03:03:42.229 if alo < ahi:
2025-07-01 03:03:42.235 if blo < bhi:
2025-07-01 03:03:42.241 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:42.246 else:
2025-07-01 03:03:42.253 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:42.261 elif blo < bhi:
2025-07-01 03:03:42.270 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:42.282
2025-07-01 03:03:42.286 >       yield from g
2025-07-01 03:03:42.291
2025-07-01 03:03:42.295 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:42.299 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:42.304
2025-07-01 03:03:42.308 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:42.313 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:42.322 alo = 336, ahi = 1101
2025-07-01 03:03:42.330 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:42.346 blo = 336, bhi = 1101
2025-07-01 03:03:42.358
2025-07-01 03:03:42.368 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:42.381 r"""
2025-07-01 03:03:42.394 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:42.399 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:42.404 synch point, and intraline difference marking is done on the
2025-07-01 03:03:42.408 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:42.413
2025-07-01 03:03:42.417 Example:
2025-07-01 03:03:42.421
2025-07-01 03:03:42.426 >>> d = Differ()
2025-07-01 03:03:42.431 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:42.436 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:42.440 >>> print(''.join(results), end="")
2025-07-01 03:03:42.445 - abcDefghiJkl
2025-07-01 03:03:42.453 + abcdefGhijkl
2025-07-01 03:03:42.462 """
2025-07-01 03:03:42.467
2025-07-01 03:03:42.471 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:42.476 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:42.481 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:42.486 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:42.492 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:42.506
2025-07-01 03:03:42.518 # search for the pair that matches best without being identical
2025-07-01 03:03:42.526 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:42.538 # on junk -- unless we have to)
2025-07-01 03:03:42.546 for j in range(blo, bhi):
2025-07-01 03:03:42.558 bj = b[j]
2025-07-01 03:03:42.570 cruncher.set_seq2(bj)
2025-07-01 03:03:42.586 for i in range(alo, ahi):
2025-07-01 03:03:42.594 ai = a[i]
2025-07-01 03:03:42.610 if ai == bj:
2025-07-01 03:03:42.618 if eqi is None:
2025-07-01 03:03:42.630 eqi, eqj = i, j
2025-07-01 03:03:42.638 continue
2025-07-01 03:03:42.647 cruncher.set_seq1(ai)
2025-07-01 03:03:42.662 # computing similarity is expensive, so use the quick
2025-07-01 03:03:42.670 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:42.681 # compares by a factor of 3.
2025-07-01 03:03:42.696 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:42.706 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:42.718 # of the computation is cached by cruncher
2025-07-01 03:03:42.730 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:42.738 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:42.746 cruncher.ratio() > best_ratio:
2025-07-01 03:03:42.760 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:42.768 if best_ratio < cutoff:
2025-07-01 03:03:42.782 # no non-identical "pretty close" pair
2025-07-01 03:03:42.794 if eqi is None:
2025-07-01 03:03:42.810 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:42.822 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:42.834 return
2025-07-01 03:03:42.846 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:42.858 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:42.866 else:
2025-07-01 03:03:42.878 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:42.894 eqi = None
2025-07-01 03:03:42.902
2025-07-01 03:03:42.918 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:42.926 # identical
2025-07-01 03:03:42.933
2025-07-01 03:03:42.946 # pump out diffs from before the synch point
2025-07-01 03:03:42.962 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:42.970
2025-07-01 03:03:42.984 # do intraline marking on the synch pair
2025-07-01 03:03:42.994 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:43.005 if eqi is None:
2025-07-01 03:03:43.017 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:43.029 atags = btags = ""
2025-07-01 03:03:43.038 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:43.050 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:43.058 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:43.066 if tag == 'replace':
2025-07-01 03:03:43.075 atags += '^' * la
2025-07-01 03:03:43.082 btags += '^' * lb
2025-07-01 03:03:43.101 elif tag == 'delete':
2025-07-01 03:03:43.117 atags += '-' * la
2025-07-01 03:03:43.130 elif tag == 'insert':
2025-07-01 03:03:43.142 btags += '+' * lb
2025-07-01 03:03:43.153 elif tag == 'equal':
2025-07-01 03:03:43.162 atags += ' ' * la
2025-07-01 03:03:43.176 btags += ' ' * lb
2025-07-01 03:03:43.185 else:
2025-07-01 03:03:43.194 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:43.210 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:43.218 else:
2025-07-01 03:03:43.230 # the synch pair is identical
2025-07-01 03:03:43.242 yield '  ' + aelt
2025-07-01 03:03:43.250
2025-07-01 03:03:43.262 # pump out diffs from after the synch point
2025-07-01 03:03:43.270 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:43.282
2025-07-01 03:03:43.290 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:43.302 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:43.314
2025-07-01 03:03:43.326 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:43.338 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:43.350 alo = 337, ahi = 1101
2025-07-01 03:03:43.359 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:43.374 blo = 337, bhi = 1101
2025-07-01 03:03:43.382
2025-07-01 03:03:43.396 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:43.410 g = []
2025-07-01 03:03:43.418 if alo < ahi:
2025-07-01 03:03:43.430 if blo < bhi:
2025-07-01 03:03:43.438 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:43.454 else:
2025-07-01 03:03:43.466 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:43.474 elif blo < bhi:
2025-07-01 03:03:43.490 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:43.502
2025-07-01 03:03:43.514 >       yield from g
2025-07-01 03:03:43.522
2025-07-01 03:03:43.534 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:43.541 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:43.549
2025-07-01 03:03:43.562 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:43.573 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:43.582 alo = 337, ahi = 1101
2025-07-01 03:03:43.598 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:43.606 blo = 337, bhi = 1101
2025-07-01 03:03:43.618
2025-07-01 03:03:43.626 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:43.642 r"""
2025-07-01 03:03:43.654 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:43.662 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:43.678 synch point, and intraline difference marking is done on the
2025-07-01 03:03:43.686 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:43.697
2025-07-01 03:03:43.710 Example:
2025-07-01 03:03:43.718
2025-07-01 03:03:43.731 >>> d = Differ()
2025-07-01 03:03:43.742 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:43.758 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:43.774 >>> print(''.join(results), end="")
2025-07-01 03:03:43.790 - abcDefghiJkl
2025-07-01 03:03:43.826 + abcdefGhijkl
2025-07-01 03:03:43.854 """
2025-07-01 03:03:43.862
2025-07-01 03:03:43.874 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:43.886 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:43.902 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:43.914 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:43.926 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:43.942
2025-07-01 03:03:43.950 # search for the pair that matches best without being identical
2025-07-01 03:03:43.962 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:43.974 # on junk -- unless we have to)
2025-07-01 03:03:43.986 for j in range(blo, bhi):
2025-07-01 03:03:43.994 bj = b[j]
2025-07-01 03:03:44.006 cruncher.set_seq2(bj)
2025-07-01 03:03:44.017 for i in range(alo, ahi):
2025-07-01 03:03:44.030 ai = a[i]
2025-07-01 03:03:44.042 if ai == bj:
2025-07-01 03:03:44.058 if eqi is None:
2025-07-01 03:03:44.069 eqi, eqj = i, j
2025-07-01 03:03:44.082 continue
2025-07-01 03:03:44.094 cruncher.set_seq1(ai)
2025-07-01 03:03:44.109 # computing similarity is expensive, so use the quick
2025-07-01 03:03:44.122 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:44.138 # compares by a factor of 3.
2025-07-01 03:03:44.146 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:44.154 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:44.170 # of the computation is cached by cruncher
2025-07-01 03:03:44.186 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:44.197 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:44.210 cruncher.ratio() > best_ratio:
2025-07-01 03:03:44.222 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:44.234 if best_ratio < cutoff:
2025-07-01 03:03:44.242 # no non-identical "pretty close" pair
2025-07-01 03:03:44.254 if eqi is None:
2025-07-01 03:03:44.262 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:44.272 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:44.286 return
2025-07-01 03:03:44.294 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:44.306 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:44.314 else:
2025-07-01 03:03:44.334 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:44.346 eqi = None
2025-07-01 03:03:44.354
2025-07-01 03:03:44.370 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:44.378 # identical
2025-07-01 03:03:44.386
2025-07-01 03:03:44.394 # pump out diffs from before the synch point
2025-07-01 03:03:44.410 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:44.418
2025-07-01 03:03:44.426 # do intraline marking on the synch pair
2025-07-01 03:03:44.438 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:44.446 if eqi is None:
2025-07-01 03:03:44.458 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:44.470 atags = btags = ""
2025-07-01 03:03:44.478 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:44.490 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:44.496 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:44.510 if tag == 'replace':
2025-07-01 03:03:44.522 atags += '^' * la
2025-07-01 03:03:44.530 btags += '^' * lb
2025-07-01 03:03:44.542 elif tag == 'delete':
2025-07-01 03:03:44.550 atags += '-' * la
2025-07-01 03:03:44.563 elif tag == 'insert':
2025-07-01 03:03:44.578 btags += '+' * lb
2025-07-01 03:03:44.585 elif tag == 'equal':
2025-07-01 03:03:44.600 atags += ' ' * la
2025-07-01 03:03:44.610 btags += ' ' * lb
2025-07-01 03:03:44.619 else:
2025-07-01 03:03:44.634 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:44.646 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:44.658 else:
2025-07-01 03:03:44.665 # the synch pair is identical
2025-07-01 03:03:44.680 yield '  ' + aelt
2025-07-01 03:03:44.690
2025-07-01 03:03:44.702 # pump out diffs from after the synch point
2025-07-01 03:03:44.710 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:44.721
2025-07-01 03:03:44.734 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:44.750 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:44.762
2025-07-01 03:03:44.777 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:44.790 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:44.802 alo = 338, ahi = 1101
2025-07-01 03:03:44.814 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:44.822 blo = 338, bhi = 1101
2025-07-01 03:03:44.833
2025-07-01 03:03:44.849 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:44.862 g = []
2025-07-01 03:03:44.878 if alo < ahi:
2025-07-01 03:03:44.890 if blo < bhi:
2025-07-01 03:03:44.906 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:44.918 else:
2025-07-01 03:03:44.938 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:44.950 elif blo < bhi:
2025-07-01 03:03:44.962 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:44.978
2025-07-01 03:03:44.986 >       yield from g
2025-07-01 03:03:44.998
2025-07-01 03:03:45.014 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:45.022 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:45.033
2025-07-01 03:03:45.046 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:45.062 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:45.077 alo = 338, ahi = 1101
2025-07-01 03:03:45.089 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:45.102 blo = 338, bhi = 1101
2025-07-01 03:03:45.118
2025-07-01 03:03:45.126 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:45.138 r"""
2025-07-01 03:03:45.146 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:45.158 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:45.170 synch point, and intraline difference marking is done on the
2025-07-01 03:03:45.178 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:45.190
2025-07-01 03:03:45.198 Example:
2025-07-01 03:03:45.210
2025-07-01 03:03:45.218 >>> d = Differ()
2025-07-01 03:03:45.234 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:45.250 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:45.262 >>> print(''.join(results), end="")
2025-07-01 03:03:45.272 - abcDefghiJkl
2025-07-01 03:03:45.294 + abcdefGhijkl
2025-07-01 03:03:45.318 """
2025-07-01 03:03:45.329
2025-07-01 03:03:45.342 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:45.350 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:45.362 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:45.372 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:45.382 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:45.398
2025-07-01 03:03:45.410 # search for the pair that matches best without being identical
2025-07-01 03:03:45.422 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:45.435 # on junk -- unless we have to)
2025-07-01 03:03:45.440 for j in range(blo, bhi):
2025-07-01 03:03:45.447 bj = b[j]
2025-07-01 03:03:45.452 cruncher.set_seq2(bj)
2025-07-01 03:03:45.457 for i in range(alo, ahi):
2025-07-01 03:03:45.462 ai = a[i]
2025-07-01 03:03:45.467 if ai == bj:
2025-07-01 03:03:45.473 if eqi is None:
2025-07-01 03:03:45.478 eqi, eqj = i, j
2025-07-01 03:03:45.483 continue
2025-07-01 03:03:45.488 cruncher.set_seq1(ai)
2025-07-01 03:03:45.494 # computing similarity is expensive, so use the quick
2025-07-01 03:03:45.499 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:45.504 # compares by a factor of 3.
2025-07-01 03:03:45.511 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:45.517 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:45.523 # of the computation is cached by cruncher
2025-07-01 03:03:45.528 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:45.534 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:45.538 cruncher.ratio() > best_ratio:
2025-07-01 03:03:45.544 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:45.550 if best_ratio < cutoff:
2025-07-01 03:03:45.557 # no non-identical "pretty close" pair
2025-07-01 03:03:45.562 if eqi is None:
2025-07-01 03:03:45.569 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:45.575 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:45.581 return
2025-07-01 03:03:45.587 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:45.592 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:45.599 else:
2025-07-01 03:03:45.604 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:45.609 eqi = None
2025-07-01 03:03:45.617
2025-07-01 03:03:45.627 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:45.635 # identical
2025-07-01 03:03:45.643
2025-07-01 03:03:45.655 # pump out diffs from before the synch point
2025-07-01 03:03:45.667 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:45.672
2025-07-01 03:03:45.677 # do intraline marking on the synch pair
2025-07-01 03:03:45.681 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:45.686 if eqi is None:
2025-07-01 03:03:45.690 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:45.695 atags = btags = ""
2025-07-01 03:03:45.700 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:45.714 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:45.725 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:45.738 if tag == 'replace':
2025-07-01 03:03:45.749 atags += '^' * la
2025-07-01 03:03:45.764 btags += '^' * lb
2025-07-01 03:03:45.778 elif tag == 'delete':
2025-07-01 03:03:45.790 atags += '-' * la
2025-07-01 03:03:45.798 elif tag == 'insert':
2025-07-01 03:03:45.810 btags += '+' * lb
2025-07-01 03:03:45.818 elif tag == 'equal':
2025-07-01 03:03:45.826 atags += ' ' * la
2025-07-01 03:03:45.840 btags += ' ' * lb
2025-07-01 03:03:45.854 else:
2025-07-01 03:03:45.866 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:45.878 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:45.889 else:
2025-07-01 03:03:45.900 # the synch pair is identical
2025-07-01 03:03:45.910 yield '  ' + aelt
2025-07-01 03:03:45.922
2025-07-01 03:03:45.934 # pump out diffs from after the synch point
2025-07-01 03:03:45.941 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:45.949
2025-07-01 03:03:45.962 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:45.974 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:45.986
2025-07-01 03:03:46.002 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:46.010 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:46.018 alo = 339, ahi = 1101
2025-07-01 03:03:46.028 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:46.045 blo = 339, bhi = 1101
2025-07-01 03:03:46.062
2025-07-01 03:03:46.070 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:46.078 g = []
2025-07-01 03:03:46.086 if alo < ahi:
2025-07-01 03:03:46.098 if blo < bhi:
2025-07-01 03:03:46.110 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:46.118 else:
2025-07-01 03:03:46.128 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:46.138 elif blo < bhi:
2025-07-01 03:03:46.154 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:46.162
2025-07-01 03:03:46.174 >       yield from g
2025-07-01 03:03:46.182
2025-07-01 03:03:46.192 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:46.206 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:46.214
2025-07-01 03:03:46.226 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:46.242 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:46.250 alo = 339, ahi = 1101
2025-07-01 03:03:46.262 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:46.270 blo = 339, bhi = 1101
2025-07-01 03:03:46.278
2025-07-01 03:03:46.294 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:46.302 r"""
2025-07-01 03:03:46.314 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:46.326 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:46.337 synch point, and intraline difference marking is done on the
2025-07-01 03:03:46.348 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:46.361
2025-07-01 03:03:46.370 Example:
2025-07-01 03:03:46.390
2025-07-01 03:03:46.406 >>> d = Differ()
2025-07-01 03:03:46.418 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:46.430 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:46.446 >>> print(''.join(results), end="")
2025-07-01 03:03:46.458 - abcDefghiJkl
2025-07-01 03:03:46.478 + abcdefGhijkl
2025-07-01 03:03:46.498 """
2025-07-01 03:03:46.506
2025-07-01 03:03:46.518 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:46.530 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:46.542 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:46.550 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:46.566 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:46.574
2025-07-01 03:03:46.590 # search for the pair that matches best without being identical
2025-07-01 03:03:46.602 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:46.610 # on junk -- unless we have to)
2025-07-01 03:03:46.621 for j in range(blo, bhi):
2025-07-01 03:03:46.631 bj = b[j]
2025-07-01 03:03:46.646 cruncher.set_seq2(bj)
2025-07-01 03:03:46.658 for i in range(alo, ahi):
2025-07-01 03:03:46.666 ai = a[i]
2025-07-01 03:03:46.674 if ai == bj:
2025-07-01 03:03:46.686 if eqi is None:
2025-07-01 03:03:46.698 eqi, eqj = i, j
2025-07-01 03:03:46.706 continue
2025-07-01 03:03:46.714 cruncher.set_seq1(ai)
2025-07-01 03:03:46.726 # computing similarity is expensive, so use the quick
2025-07-01 03:03:46.734 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:46.746 # compares by a factor of 3.
2025-07-01 03:03:46.754 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:46.766 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:46.778 # of the computation is cached by cruncher
2025-07-01 03:03:46.790 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:46.797 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:46.814 cruncher.ratio() > best_ratio:
2025-07-01 03:03:46.822 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:46.838 if best_ratio < cutoff:
2025-07-01 03:03:46.844 # no non-identical "pretty close" pair
2025-07-01 03:03:46.856 if eqi is None:
2025-07-01 03:03:46.867 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:46.890 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:46.898 return
2025-07-01 03:03:46.907 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:46.917 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:46.933 else:
2025-07-01 03:03:46.941 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:46.947 eqi = None
2025-07-01 03:03:46.953
2025-07-01 03:03:46.959 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:46.966 # identical
2025-07-01 03:03:46.972
2025-07-01 03:03:46.976 # pump out diffs from before the synch point
2025-07-01 03:03:46.983 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:46.989
2025-07-01 03:03:46.993 # do intraline marking on the synch pair
2025-07-01 03:03:46.999 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:47.006 if eqi is None:
2025-07-01 03:03:47.014 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:47.023 atags = btags = ""
2025-07-01 03:03:47.030 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:47.038 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:47.044 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:47.052 if tag == 'replace':
2025-07-01 03:03:47.059 atags += '^' * la
2025-07-01 03:03:47.063 btags += '^' * lb
2025-07-01 03:03:47.069 elif tag == 'delete':
2025-07-01 03:03:47.076 atags += '-' * la
2025-07-01 03:03:47.083 elif tag == 'insert':
2025-07-01 03:03:47.090 btags += '+' * lb
2025-07-01 03:03:47.098 elif tag == 'equal':
2025-07-01 03:03:47.110 atags += ' ' * la
2025-07-01 03:03:47.118 btags += ' ' * lb
2025-07-01 03:03:47.124 else:
2025-07-01 03:03:47.129 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:47.134 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:47.138 else:
2025-07-01 03:03:47.142 # the synch pair is identical
2025-07-01 03:03:47.147 yield '  ' + aelt
2025-07-01 03:03:47.151
2025-07-01 03:03:47.157 # pump out diffs from after the synch point
2025-07-01 03:03:47.162 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:47.178
2025-07-01 03:03:47.186 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:47.198 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:47.210
2025-07-01 03:03:47.222 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:47.229 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:47.242 alo = 340, ahi = 1101
2025-07-01 03:03:47.257 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:47.270 blo = 340, bhi = 1101
2025-07-01 03:03:47.278
2025-07-01 03:03:47.294 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:47.302 g = []
2025-07-01 03:03:47.310 if alo < ahi:
2025-07-01 03:03:47.326 if blo < bhi:
2025-07-01 03:03:47.334 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:47.350 else:
2025-07-01 03:03:47.358 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:47.370 elif blo < bhi:
2025-07-01 03:03:47.378 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:47.394
2025-07-01 03:03:47.402 >       yield from g
2025-07-01 03:03:47.412
2025-07-01 03:03:47.422 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:47.434 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:47.446
2025-07-01 03:03:47.454 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:47.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:47.478 alo = 340, ahi = 1101
2025-07-01 03:03:47.490 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:47.506 blo = 340, bhi = 1101
2025-07-01 03:03:47.514
2025-07-01 03:03:47.530 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:47.538 r"""
2025-07-01 03:03:47.550 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:47.560 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:47.578 synch point, and intraline difference marking is done on the
2025-07-01 03:03:47.590 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:47.598
2025-07-01 03:03:47.606 Example:
2025-07-01 03:03:47.621
2025-07-01 03:03:47.630 >>> d = Differ()
2025-07-01 03:03:47.646 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:47.654 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:47.666 >>> print(''.join(results), end="")
2025-07-01 03:03:47.674 - abcDefghiJkl
2025-07-01 03:03:47.699 + abcdefGhijkl
2025-07-01 03:03:47.722 """
2025-07-01 03:03:47.734
2025-07-01 03:03:47.742 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:47.754 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:47.766 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:47.774 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:47.786 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:47.793
2025-07-01 03:03:47.810 # search for the pair that matches best without being identical
2025-07-01 03:03:47.821 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:47.834 # on junk -- unless we have to)
2025-07-01 03:03:47.842 for j in range(blo, bhi):
2025-07-01 03:03:47.858 bj = b[j]
2025-07-01 03:03:47.867 cruncher.set_seq2(bj)
2025-07-01 03:03:47.886 for i in range(alo, ahi):
2025-07-01 03:03:47.898 ai = a[i]
2025-07-01 03:03:47.905 if ai == bj:
2025-07-01 03:03:47.922 if eqi is None:
2025-07-01 03:03:47.934 eqi, eqj = i, j
2025-07-01 03:03:47.942 continue
2025-07-01 03:03:47.954 cruncher.set_seq1(ai)
2025-07-01 03:03:47.966 # computing similarity is expensive, so use the quick
2025-07-01 03:03:47.973 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:47.990 # compares by a factor of 3.
2025-07-01 03:03:48.002 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:48.010 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:48.022 # of the computation is cached by cruncher
2025-07-01 03:03:48.030 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:48.046 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:48.052 cruncher.ratio() > best_ratio:
2025-07-01 03:03:48.061 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:48.073 if best_ratio < cutoff:
2025-07-01 03:03:48.087 # no non-identical "pretty close" pair
2025-07-01 03:03:48.098 if eqi is None:
2025-07-01 03:03:48.112 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:48.126 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:48.137 return
2025-07-01 03:03:48.153 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:48.160 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:48.166 else:
2025-07-01 03:03:48.173 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:48.179 eqi = None
2025-07-01 03:03:48.186
2025-07-01 03:03:48.197 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:48.202 # identical
2025-07-01 03:03:48.206
2025-07-01 03:03:48.211 # pump out diffs from before the synch point
2025-07-01 03:03:48.226 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:48.234
2025-07-01 03:03:48.242 # do intraline marking on the synch pair
2025-07-01 03:03:48.254 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:48.261 if eqi is None:
2025-07-01 03:03:48.270 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:48.282 atags = btags = ""
2025-07-01 03:03:48.290 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:48.302 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:48.314 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:48.322 if tag == 'replace':
2025-07-01 03:03:48.334 atags += '^' * la
2025-07-01 03:03:48.342 btags += '^' * lb
2025-07-01 03:03:48.353 elif tag == 'delete':
2025-07-01 03:03:48.370 atags += '-' * la
2025-07-01 03:03:48.389 elif tag == 'insert':
2025-07-01 03:03:48.398 btags += '+' * lb
2025-07-01 03:03:48.406 elif tag == 'equal':
2025-07-01 03:03:48.418 atags += ' ' * la
2025-07-01 03:03:48.426 btags += ' ' * lb
2025-07-01 03:03:48.442 else:
2025-07-01 03:03:48.453 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:48.466 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:48.474 else:
2025-07-01 03:03:48.486 # the synch pair is identical
2025-07-01 03:03:48.502 yield '  ' + aelt
2025-07-01 03:03:48.510
2025-07-01 03:03:48.522 # pump out diffs from after the synch point
2025-07-01 03:03:48.534 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:48.542
2025-07-01 03:03:48.558 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:48.570 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:48.582
2025-07-01 03:03:48.601 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:48.618 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:48.634 alo = 341, ahi = 1101
2025-07-01 03:03:48.646 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:48.654 blo = 341, bhi = 1101
2025-07-01 03:03:48.661
2025-07-01 03:03:48.674 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:48.682 g = []
2025-07-01 03:03:48.694 if alo < ahi:
2025-07-01 03:03:48.705 if blo < bhi:
2025-07-01 03:03:48.718 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:48.726 else:
2025-07-01 03:03:48.738 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:48.750 elif blo < bhi:
2025-07-01 03:03:48.762 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:48.770
2025-07-01 03:03:48.785 >       yield from g
2025-07-01 03:03:48.794
2025-07-01 03:03:48.802 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:48.810 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:48.822
2025-07-01 03:03:48.829 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:48.838 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:48.846 alo = 341, ahi = 1101
2025-07-01 03:03:48.865 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:48.876 blo = 341, bhi = 1101
2025-07-01 03:03:48.885
2025-07-01 03:03:48.898 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:48.906 r"""
2025-07-01 03:03:48.918 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:48.926 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:48.934 synch point, and intraline difference marking is done on the
2025-07-01 03:03:48.946 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:48.958
2025-07-01 03:03:48.970 Example:
2025-07-01 03:03:48.979
2025-07-01 03:03:48.990 >>> d = Differ()
2025-07-01 03:03:49.006 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:49.017 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:49.030 >>> print(''.join(results), end="")
2025-07-01 03:03:49.049 - abcDefghiJkl
2025-07-01 03:03:49.082 + abcdefGhijkl
2025-07-01 03:03:49.102 """
2025-07-01 03:03:49.114
2025-07-01 03:03:49.126 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:49.136 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:49.148 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:49.160 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:49.178 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:49.190
2025-07-01 03:03:49.202 # search for the pair that matches best without being identical
2025-07-01 03:03:49.216 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:49.226 # on junk -- unless we have to)
2025-07-01 03:03:49.236 for j in range(blo, bhi):
2025-07-01 03:03:49.250 bj = b[j]
2025-07-01 03:03:49.262 cruncher.set_seq2(bj)
2025-07-01 03:03:49.272 for i in range(alo, ahi):
2025-07-01 03:03:49.288 ai = a[i]
2025-07-01 03:03:49.306 if ai == bj:
2025-07-01 03:03:49.314 if eqi is None:
2025-07-01 03:03:49.326 eqi, eqj = i, j
2025-07-01 03:03:49.334 continue
2025-07-01 03:03:49.346 cruncher.set_seq1(ai)
2025-07-01 03:03:49.354 # computing similarity is expensive, so use the quick
2025-07-01 03:03:49.366 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:49.372 # compares by a factor of 3.
2025-07-01 03:03:49.377 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:49.382 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:49.387 # of the computation is cached by cruncher
2025-07-01 03:03:49.392 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:49.397 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:49.402 cruncher.ratio() > best_ratio:
2025-07-01 03:03:49.406 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:49.411 if best_ratio < cutoff:
2025-07-01 03:03:49.415 # no non-identical "pretty close" pair
2025-07-01 03:03:49.420 if eqi is None:
2025-07-01 03:03:49.425 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:49.429 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:49.435 return
2025-07-01 03:03:49.439 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:49.444 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:49.449 else:
2025-07-01 03:03:49.453 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:49.458 eqi = None
2025-07-01 03:03:49.462
2025-07-01 03:03:49.467 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:49.471 # identical
2025-07-01 03:03:49.476
2025-07-01 03:03:49.481 # pump out diffs from before the synch point
2025-07-01 03:03:49.485 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:49.490
2025-07-01 03:03:49.495 # do intraline marking on the synch pair
2025-07-01 03:03:49.499 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:49.504 if eqi is None:
2025-07-01 03:03:49.509 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:49.513 atags = btags = ""
2025-07-01 03:03:49.518 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:49.523 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:49.527 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:49.532 if tag == 'replace':
2025-07-01 03:03:49.536 atags += '^' * la
2025-07-01 03:03:49.541 btags += '^' * lb
2025-07-01 03:03:49.545 elif tag == 'delete':
2025-07-01 03:03:49.551 atags += '-' * la
2025-07-01 03:03:49.557 elif tag == 'insert':
2025-07-01 03:03:49.561 btags += '+' * lb
2025-07-01 03:03:49.566 elif tag == 'equal':
2025-07-01 03:03:49.571 atags += ' ' * la
2025-07-01 03:03:49.575 btags += ' ' * lb
2025-07-01 03:03:49.579 else:
2025-07-01 03:03:49.584 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:49.589 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:49.593 else:
2025-07-01 03:03:49.598 # the synch pair is identical
2025-07-01 03:03:49.603 yield '  ' + aelt
2025-07-01 03:03:49.607
2025-07-01 03:03:49.612 # pump out diffs from after the synch point
2025-07-01 03:03:49.616 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:49.621
2025-07-01 03:03:49.625 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:49.630 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:49.634
2025-07-01 03:03:49.639 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:49.644 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:49.648 alo = 342, ahi = 1101
2025-07-01 03:03:49.654 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:49.658 blo = 342, bhi = 1101
2025-07-01 03:03:49.662
2025-07-01 03:03:49.667 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:49.671 g = []
2025-07-01 03:03:49.676 if alo < ahi:
2025-07-01 03:03:49.680 if blo < bhi:
2025-07-01 03:03:49.685 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:49.690 else:
2025-07-01 03:03:49.694 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:49.698 elif blo < bhi:
2025-07-01 03:03:49.703 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:49.707
2025-07-01 03:03:49.711 >       yield from g
2025-07-01 03:03:49.715
2025-07-01 03:03:49.720 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:49.724 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:49.729
2025-07-01 03:03:49.733 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:49.738 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:49.742 alo = 342, ahi = 1101
2025-07-01 03:03:49.747 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:49.751 blo = 342, bhi = 1101
2025-07-01 03:03:49.755
2025-07-01 03:03:49.760 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:49.764 r"""
2025-07-01 03:03:49.768 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:49.773 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:49.777 synch point, and intraline difference marking is done on the
2025-07-01 03:03:49.782 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:49.786
2025-07-01 03:03:49.791 Example:
2025-07-01 03:03:49.795
2025-07-01 03:03:49.799 >>> d = Differ()
2025-07-01 03:03:49.805 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:49.810 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:49.815 >>> print(''.join(results), end="")
2025-07-01 03:03:49.821 - abcDefghiJkl
2025-07-01 03:03:49.831 + abcdefGhijkl
2025-07-01 03:03:49.842 """
2025-07-01 03:03:49.848
2025-07-01 03:03:49.853 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:49.858 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:49.863 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:49.868 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:49.872 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:49.877
2025-07-01 03:03:49.881 # search for the pair that matches best without being identical
2025-07-01 03:03:49.886 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:49.890 # on junk -- unless we have to)
2025-07-01 03:03:49.894 for j in range(blo, bhi):
2025-07-01 03:03:49.899 bj = b[j]
2025-07-01 03:03:49.903 cruncher.set_seq2(bj)
2025-07-01 03:03:49.909 for i in range(alo, ahi):
2025-07-01 03:03:49.913 ai = a[i]
2025-07-01 03:03:49.917 if ai == bj:
2025-07-01 03:03:49.922 if eqi is None:
2025-07-01 03:03:49.926 eqi, eqj = i, j
2025-07-01 03:03:49.930 continue
2025-07-01 03:03:49.935 cruncher.set_seq1(ai)
2025-07-01 03:03:49.939 # computing similarity is expensive, so use the quick
2025-07-01 03:03:49.944 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:49.948 # compares by a factor of 3.
2025-07-01 03:03:49.952 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:49.957 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:49.961 # of the computation is cached by cruncher
2025-07-01 03:03:49.965 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:49.970 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:49.975 cruncher.ratio() > best_ratio:
2025-07-01 03:03:49.980 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:49.984 if best_ratio < cutoff:
2025-07-01 03:03:50.002 # no non-identical "pretty close" pair
2025-07-01 03:03:50.016 if eqi is None:
2025-07-01 03:03:50.026 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:50.046 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:50.054 return
2025-07-01 03:03:50.066 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:50.075 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:50.086 else:
2025-07-01 03:03:50.098 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:50.110 eqi = None
2025-07-01 03:03:50.118
2025-07-01 03:03:50.130 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:50.138 # identical
2025-07-01 03:03:50.150
2025-07-01 03:03:50.162 # pump out diffs from before the synch point
2025-07-01 03:03:50.170 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:50.182
2025-07-01 03:03:50.190 # do intraline marking on the synch pair
2025-07-01 03:03:50.202 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:50.210 if eqi is None:
2025-07-01 03:03:50.226 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:50.234 atags = btags = ""
2025-07-01 03:03:50.246 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:50.254 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:50.266 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:50.274 if tag == 'replace':
2025-07-01 03:03:50.286 atags += '^' * la
2025-07-01 03:03:50.298 btags += '^' * lb
2025-07-01 03:03:50.306 elif tag == 'delete':
2025-07-01 03:03:50.318 atags += '-' * la
2025-07-01 03:03:50.334 elif tag == 'insert':
2025-07-01 03:03:50.344 btags += '+' * lb
2025-07-01 03:03:50.354 elif tag == 'equal':
2025-07-01 03:03:50.366 atags += ' ' * la
2025-07-01 03:03:50.374 btags += ' ' * lb
2025-07-01 03:03:50.386 else:
2025-07-01 03:03:50.398 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:50.412 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:50.422 else:
2025-07-01 03:03:50.441 # the synch pair is identical
2025-07-01 03:03:50.454 yield '  ' + aelt
2025-07-01 03:03:50.465
2025-07-01 03:03:50.478 # pump out diffs from after the synch point
2025-07-01 03:03:50.497 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:50.512
2025-07-01 03:03:50.522 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:50.534 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:50.550
2025-07-01 03:03:50.573 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:50.586 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:50.595 alo = 343, ahi = 1101
2025-07-01 03:03:50.611 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:50.619 blo = 343, bhi = 1101
2025-07-01 03:03:50.625
2025-07-01 03:03:50.640 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:50.651 g = []
2025-07-01 03:03:50.665 if alo < ahi:
2025-07-01 03:03:50.674 if blo < bhi:
2025-07-01 03:03:50.687 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:50.706 else:
2025-07-01 03:03:50.717 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:50.726 elif blo < bhi:
2025-07-01 03:03:50.749 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:50.759
2025-07-01 03:03:50.773 >       yield from g
2025-07-01 03:03:50.786
2025-07-01 03:03:50.797 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:50.806 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:50.814
2025-07-01 03:03:50.830 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:50.846 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:50.854 alo = 343, ahi = 1101
2025-07-01 03:03:50.866 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:50.874 blo = 343, bhi = 1101
2025-07-01 03:03:50.889
2025-07-01 03:03:50.898 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:50.906 r"""
2025-07-01 03:03:50.914 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:50.930 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:50.938 synch point, and intraline difference marking is done on the
2025-07-01 03:03:50.950 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:50.958
2025-07-01 03:03:50.970 Example:
2025-07-01 03:03:50.982
2025-07-01 03:03:50.994 >>> d = Differ()
2025-07-01 03:03:51.003 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:51.014 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:51.026 >>> print(''.join(results), end="")
2025-07-01 03:03:51.038 - abcDefghiJkl
2025-07-01 03:03:51.058 + abcdefGhijkl
2025-07-01 03:03:51.078 """
2025-07-01 03:03:51.094
2025-07-01 03:03:51.102 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:51.118 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:51.126 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:51.142 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:51.150 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:51.162
2025-07-01 03:03:51.170 # search for the pair that matches best without being identical
2025-07-01 03:03:51.182 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:51.190 # on junk -- unless we have to)
2025-07-01 03:03:51.202 for j in range(blo, bhi):
2025-07-01 03:03:51.209 bj = b[j]
2025-07-01 03:03:51.226 cruncher.set_seq2(bj)
2025-07-01 03:03:51.234 for i in range(alo, ahi):
2025-07-01 03:03:51.250 ai = a[i]
2025-07-01 03:03:51.258 if ai == bj:
2025-07-01 03:03:51.270 if eqi is None:
2025-07-01 03:03:51.278 eqi, eqj = i, j
2025-07-01 03:03:51.291 continue
2025-07-01 03:03:51.302 cruncher.set_seq1(ai)
2025-07-01 03:03:51.312 # computing similarity is expensive, so use the quick
2025-07-01 03:03:51.330 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:51.338 # compares by a factor of 3.
2025-07-01 03:03:51.350 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:51.358 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:51.365 # of the computation is cached by cruncher
2025-07-01 03:03:51.380 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:51.390 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:51.400 cruncher.ratio() > best_ratio:
2025-07-01 03:03:51.410 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:51.420 if best_ratio < cutoff:
2025-07-01 03:03:51.434 # no non-identical "pretty close" pair
2025-07-01 03:03:51.446 if eqi is None:
2025-07-01 03:03:51.454 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:51.466 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:51.476 return
2025-07-01 03:03:51.488 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:51.498 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:51.518 else:
2025-07-01 03:03:51.531 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:51.550 eqi = None
2025-07-01 03:03:51.566
2025-07-01 03:03:51.574 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:51.586 # identical
2025-07-01 03:03:51.598
2025-07-01 03:03:51.610 # pump out diffs from before the synch point
2025-07-01 03:03:51.626 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:51.638
2025-07-01 03:03:51.646 # do intraline marking on the synch pair
2025-07-01 03:03:51.654 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:51.668 if eqi is None:
2025-07-01 03:03:51.677 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:51.690 atags = btags = ""
2025-07-01 03:03:51.698 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:51.709 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:51.718 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:51.730 if tag == 'replace':
2025-07-01 03:03:51.738 atags += '^' * la
2025-07-01 03:03:51.750 btags += '^' * lb
2025-07-01 03:03:51.758 elif tag == 'delete':
2025-07-01 03:03:51.770 atags += '-' * la
2025-07-01 03:03:51.778 elif tag == 'insert':
2025-07-01 03:03:51.792 btags += '+' * lb
2025-07-01 03:03:51.802 elif tag == 'equal':
2025-07-01 03:03:51.814 atags += ' ' * la
2025-07-01 03:03:51.828 btags += ' ' * lb
2025-07-01 03:03:51.842 else:
2025-07-01 03:03:51.858 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:51.869 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:51.882 else:
2025-07-01 03:03:51.894 # the synch pair is identical
2025-07-01 03:03:51.901 yield '  ' + aelt
2025-07-01 03:03:51.918
2025-07-01 03:03:51.925 # pump out diffs from after the synch point
2025-07-01 03:03:51.937 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:51.954
2025-07-01 03:03:51.966 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:51.974 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:51.984
2025-07-01 03:03:51.994 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:52.009 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:52.021 alo = 344, ahi = 1101
2025-07-01 03:03:52.041 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:52.051 blo = 344, bhi = 1101
2025-07-01 03:03:52.059
2025-07-01 03:03:52.081 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:52.088 g = []
2025-07-01 03:03:52.106 if alo < ahi:
2025-07-01 03:03:52.112 if blo < bhi:
2025-07-01 03:03:52.129 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:52.141 else:
2025-07-01 03:03:52.157 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:52.170 elif blo < bhi:
2025-07-01 03:03:52.185 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:52.201
2025-07-01 03:03:52.214 >       yield from g
2025-07-01 03:03:52.229
2025-07-01 03:03:52.242 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:52.257 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:52.265
2025-07-01 03:03:52.281 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:52.299 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:52.314 alo = 344, ahi = 1101
2025-07-01 03:03:52.330 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:52.346 blo = 344, bhi = 1101
2025-07-01 03:03:52.363
2025-07-01 03:03:52.378 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:52.396 r"""
2025-07-01 03:03:52.405 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:52.422 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:52.430 synch point, and intraline difference marking is done on the
2025-07-01 03:03:52.442 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:52.458
2025-07-01 03:03:52.466 Example:
2025-07-01 03:03:52.478
2025-07-01 03:03:52.486 >>> d = Differ()
2025-07-01 03:03:52.502 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:52.509 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:52.522 >>> print(''.join(results), end="")
2025-07-01 03:03:52.530 - abcDefghiJkl
2025-07-01 03:03:52.554 + abcdefGhijkl
2025-07-01 03:03:52.574 """
2025-07-01 03:03:52.586
2025-07-01 03:03:52.598 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:52.610 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:52.622 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:52.634 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:52.641 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:52.658
2025-07-01 03:03:52.670 # search for the pair that matches best without being identical
2025-07-01 03:03:52.682 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:52.694 # on junk -- unless we have to)
2025-07-01 03:03:52.706 for j in range(blo, bhi):
2025-07-01 03:03:52.714 bj = b[j]
2025-07-01 03:03:52.730 cruncher.set_seq2(bj)
2025-07-01 03:03:52.742 for i in range(alo, ahi):
2025-07-01 03:03:52.750 ai = a[i]
2025-07-01 03:03:52.762 if ai == bj:
2025-07-01 03:03:52.774 if eqi is None:
2025-07-01 03:03:52.782 eqi, eqj = i, j
2025-07-01 03:03:52.797 continue
2025-07-01 03:03:52.814 cruncher.set_seq1(ai)
2025-07-01 03:03:52.822 # computing similarity is expensive, so use the quick
2025-07-01 03:03:52.838 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:52.846 # compares by a factor of 3.
2025-07-01 03:03:52.862 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:52.870 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:52.886 # of the computation is cached by cruncher
2025-07-01 03:03:52.894 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:52.910 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:52.918 cruncher.ratio() > best_ratio:
2025-07-01 03:03:52.934 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:52.942 if best_ratio < cutoff:
2025-07-01 03:03:52.958 # no non-identical "pretty close" pair
2025-07-01 03:03:52.966 if eqi is None:
2025-07-01 03:03:52.982 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:52.990 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:53.003 return
2025-07-01 03:03:53.022 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:53.038 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:53.050 else:
2025-07-01 03:03:53.058 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:53.070 eqi = None
2025-07-01 03:03:53.082
2025-07-01 03:03:53.092 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:53.102 # identical
2025-07-01 03:03:53.115
2025-07-01 03:03:53.130 # pump out diffs from before the synch point
2025-07-01 03:03:53.138 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:53.154
2025-07-01 03:03:53.162 # do intraline marking on the synch pair
2025-07-01 03:03:53.182 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:53.192 if eqi is None:
2025-07-01 03:03:53.202 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:53.214 atags = btags = ""
2025-07-01 03:03:53.226 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:53.238 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:53.250 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:53.258 if tag == 'replace':
2025-07-01 03:03:53.272 atags += '^' * la
2025-07-01 03:03:53.282 btags += '^' * lb
2025-07-01 03:03:53.298 elif tag == 'delete':
2025-07-01 03:03:53.306 atags += '-' * la
2025-07-01 03:03:53.322 elif tag == 'insert':
2025-07-01 03:03:53.330 btags += '+' * lb
2025-07-01 03:03:53.346 elif tag == 'equal':
2025-07-01 03:03:53.354 atags += ' ' * la
2025-07-01 03:03:53.368 btags += ' ' * lb
2025-07-01 03:03:53.378 else:
2025-07-01 03:03:53.394 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:53.406 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:53.422 else:
2025-07-01 03:03:53.430 # the synch pair is identical
2025-07-01 03:03:53.442 yield '  ' + aelt
2025-07-01 03:03:53.450
2025-07-01 03:03:53.462 # pump out diffs from after the synch point
2025-07-01 03:03:53.478 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:53.490
2025-07-01 03:03:53.498 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:53.517 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:53.526
2025-07-01 03:03:53.542 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:53.558 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:53.566 alo = 345, ahi = 1101
2025-07-01 03:03:53.582 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:53.594 blo = 345, bhi = 1101
2025-07-01 03:03:53.618
2025-07-01 03:03:53.626 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:53.634 g = []
2025-07-01 03:03:53.650 if alo < ahi:
2025-07-01 03:03:53.658 if blo < bhi:
2025-07-01 03:03:53.666 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:53.674 else:
2025-07-01 03:03:53.685 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:53.700 elif blo < bhi:
2025-07-01 03:03:53.710 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:53.718
2025-07-01 03:03:53.730 >       yield from g
2025-07-01 03:03:53.741
2025-07-01 03:03:53.754 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:53.762 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:53.770
2025-07-01 03:03:53.781 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:53.803 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:53.811 alo = 345, ahi = 1101
2025-07-01 03:03:53.830 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:53.845 blo = 345, bhi = 1101
2025-07-01 03:03:53.854
2025-07-01 03:03:53.869 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:53.882 r"""
2025-07-01 03:03:53.890 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:53.898 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:53.914 synch point, and intraline difference marking is done on the
2025-07-01 03:03:53.926 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:53.941
2025-07-01 03:03:53.947 Example:
2025-07-01 03:03:53.961
2025-07-01 03:03:53.972 >>> d = Differ()
2025-07-01 03:03:53.988 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:54.008 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:54.021 >>> print(''.join(results), end="")
2025-07-01 03:03:54.038 - abcDefghiJkl
2025-07-01 03:03:54.062 + abcdefGhijkl
2025-07-01 03:03:54.082 """
2025-07-01 03:03:54.099
2025-07-01 03:03:54.114 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:54.126 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:54.134 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:54.146 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:54.158 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:54.166
2025-07-01 03:03:54.178 # search for the pair that matches best without being identical
2025-07-01 03:03:54.184 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:54.202 # on junk -- unless we have to)
2025-07-01 03:03:54.210 for j in range(blo, bhi):
2025-07-01 03:03:54.225 bj = b[j]
2025-07-01 03:03:54.234 cruncher.set_seq2(bj)
2025-07-01 03:03:54.249 for i in range(alo, ahi):
2025-07-01 03:03:54.261 ai = a[i]
2025-07-01 03:03:54.272 if ai == bj:
2025-07-01 03:03:54.282 if eqi is None:
2025-07-01 03:03:54.290 eqi, eqj = i, j
2025-07-01 03:03:54.302 continue
2025-07-01 03:03:54.310 cruncher.set_seq1(ai)
2025-07-01 03:03:54.326 # computing similarity is expensive, so use the quick
2025-07-01 03:03:54.332 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:54.337 # compares by a factor of 3.
2025-07-01 03:03:54.341 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:54.346 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:54.350 # of the computation is cached by cruncher
2025-07-01 03:03:54.356 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:54.361 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:54.366 cruncher.ratio() > best_ratio:
2025-07-01 03:03:54.371 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:54.375 if best_ratio < cutoff:
2025-07-01 03:03:54.380 # no non-identical "pretty close" pair
2025-07-01 03:03:54.385 if eqi is None:
2025-07-01 03:03:54.390 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:54.395 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:54.399 return
2025-07-01 03:03:54.404 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:54.409 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:54.413 else:
2025-07-01 03:03:54.418 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:54.423 eqi = None
2025-07-01 03:03:54.428
2025-07-01 03:03:54.433 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:54.437 # identical
2025-07-01 03:03:54.442
2025-07-01 03:03:54.446 # pump out diffs from before the synch point
2025-07-01 03:03:54.451 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:54.456
2025-07-01 03:03:54.461 # do intraline marking on the synch pair
2025-07-01 03:03:54.465 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:54.470 if eqi is None:
2025-07-01 03:03:54.475 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:54.479 atags = btags = ""
2025-07-01 03:03:54.485 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:54.491 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:54.497 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:54.504 if tag == 'replace':
2025-07-01 03:03:54.510 atags += '^' * la
2025-07-01 03:03:54.517 btags += '^' * lb
2025-07-01 03:03:54.523 elif tag == 'delete':
2025-07-01 03:03:54.529 atags += '-' * la
2025-07-01 03:03:54.535 elif tag == 'insert':
2025-07-01 03:03:54.540 btags += '+' * lb
2025-07-01 03:03:54.545 elif tag == 'equal':
2025-07-01 03:03:54.549 atags += ' ' * la
2025-07-01 03:03:54.554 btags += ' ' * lb
2025-07-01 03:03:54.558 else:
2025-07-01 03:03:54.563 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:54.567 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:54.571 else:
2025-07-01 03:03:54.576 # the synch pair is identical
2025-07-01 03:03:54.580 yield '  ' + aelt
2025-07-01 03:03:54.585
2025-07-01 03:03:54.589 # pump out diffs from after the synch point
2025-07-01 03:03:54.594 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:54.600
2025-07-01 03:03:54.604 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:54.609 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:54.613
2025-07-01 03:03:54.618 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:54.625 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:54.630 alo = 346, ahi = 1101
2025-07-01 03:03:54.635 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:54.640 blo = 346, bhi = 1101
2025-07-01 03:03:54.644
2025-07-01 03:03:54.649 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:54.654 g = []
2025-07-01 03:03:54.659 if alo < ahi:
2025-07-01 03:03:54.663 if blo < bhi:
2025-07-01 03:03:54.668 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:54.673 else:
2025-07-01 03:03:54.678 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:54.682 elif blo < bhi:
2025-07-01 03:03:54.687 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:54.691
2025-07-01 03:03:54.695 >       yield from g
2025-07-01 03:03:54.700
2025-07-01 03:03:54.704 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:54.709 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:54.713
2025-07-01 03:03:54.718 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:54.723 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:54.727 alo = 346, ahi = 1101
2025-07-01 03:03:54.732 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:54.737 blo = 346, bhi = 1101
2025-07-01 03:03:54.741
2025-07-01 03:03:54.746 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:54.750 r"""
2025-07-01 03:03:54.754 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:54.759 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:54.763 synch point, and intraline difference marking is done on the
2025-07-01 03:03:54.768 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:54.772
2025-07-01 03:03:54.776 Example:
2025-07-01 03:03:54.781
2025-07-01 03:03:54.785 >>> d = Differ()
2025-07-01 03:03:54.789 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:54.794 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:54.798 >>> print(''.join(results), end="")
2025-07-01 03:03:54.802 - abcDefghiJkl
2025-07-01 03:03:54.811 + abcdefGhijkl
2025-07-01 03:03:54.820 """
2025-07-01 03:03:54.830
2025-07-01 03:03:54.852 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:54.865 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:54.877 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:54.890 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:54.901 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:54.914
2025-07-01 03:03:54.924 # search for the pair that matches best without being identical
2025-07-01 03:03:54.935 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:54.952 # on junk -- unless we have to)
2025-07-01 03:03:54.964 for j in range(blo, bhi):
2025-07-01 03:03:54.974 bj = b[j]
2025-07-01 03:03:54.990 cruncher.set_seq2(bj)
2025-07-01 03:03:54.998 for i in range(alo, ahi):
2025-07-01 03:03:55.013 ai = a[i]
2025-07-01 03:03:55.025 if ai == bj:
2025-07-01 03:03:55.041 if eqi is None:
2025-07-01 03:03:55.054 eqi, eqj = i, j
2025-07-01 03:03:55.066 continue
2025-07-01 03:03:55.077 cruncher.set_seq1(ai)
2025-07-01 03:03:55.089 # computing similarity is expensive, so use the quick
2025-07-01 03:03:55.106 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:55.122 # compares by a factor of 3.
2025-07-01 03:03:55.130 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:55.147 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:55.161 # of the computation is cached by cruncher
2025-07-01 03:03:55.173 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:55.189 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:55.206 cruncher.ratio() > best_ratio:
2025-07-01 03:03:55.215 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:55.229 if best_ratio < cutoff:
2025-07-01 03:03:55.250 # no non-identical "pretty close" pair
2025-07-01 03:03:55.261 if eqi is None:
2025-07-01 03:03:55.278 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:55.286 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:55.293 return
2025-07-01 03:03:55.306 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:55.318 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:55.330 else:
2025-07-01 03:03:55.339 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:55.350 eqi = None
2025-07-01 03:03:55.362
2025-07-01 03:03:55.378 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:55.391 # identical
2025-07-01 03:03:55.406
2025-07-01 03:03:55.418 # pump out diffs from before the synch point
2025-07-01 03:03:55.430 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:55.445
2025-07-01 03:03:55.461 # do intraline marking on the synch pair
2025-07-01 03:03:55.478 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:55.488 if eqi is None:
2025-07-01 03:03:55.498 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:55.514 atags = btags = ""
2025-07-01 03:03:55.522 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:55.534 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:55.546 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:55.554 if tag == 'replace':
2025-07-01 03:03:55.566 atags += '^' * la
2025-07-01 03:03:55.574 btags += '^' * lb
2025-07-01 03:03:55.586 elif tag == 'delete':
2025-07-01 03:03:55.596 atags += '-' * la
2025-07-01 03:03:55.610 elif tag == 'insert':
2025-07-01 03:03:55.622 btags += '+' * lb
2025-07-01 03:03:55.630 elif tag == 'equal':
2025-07-01 03:03:55.642 atags += ' ' * la
2025-07-01 03:03:55.650 btags += ' ' * lb
2025-07-01 03:03:55.662 else:
2025-07-01 03:03:55.670 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:55.682 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:55.690 else:
2025-07-01 03:03:55.706 # the synch pair is identical
2025-07-01 03:03:55.714 yield '  ' + aelt
2025-07-01 03:03:55.726
2025-07-01 03:03:55.738 # pump out diffs from after the synch point
2025-07-01 03:03:55.746 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:55.762
2025-07-01 03:03:55.770 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:55.786 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:55.794
2025-07-01 03:03:55.810 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:55.829 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:55.838 alo = 347, ahi = 1101
2025-07-01 03:03:55.850 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:55.862 blo = 347, bhi = 1101
2025-07-01 03:03:55.870
2025-07-01 03:03:55.882 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:55.896 g = []
2025-07-01 03:03:55.902 if alo < ahi:
2025-07-01 03:03:55.906 if blo < bhi:
2025-07-01 03:03:55.911 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:55.916 else:
2025-07-01 03:03:55.921 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:55.926 elif blo < bhi:
2025-07-01 03:03:55.930 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:55.935
2025-07-01 03:03:55.939 >       yield from g
2025-07-01 03:03:55.943
2025-07-01 03:03:55.948 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:55.952 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:55.957
2025-07-01 03:03:55.961 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:55.967 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:55.971 alo = 347, ahi = 1101
2025-07-01 03:03:55.976 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:55.981 blo = 347, bhi = 1101
2025-07-01 03:03:55.985
2025-07-01 03:03:55.990 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:55.994 r"""
2025-07-01 03:03:55.998 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:56.003 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:56.008 synch point, and intraline difference marking is done on the
2025-07-01 03:03:56.012 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:56.017
2025-07-01 03:03:56.021 Example:
2025-07-01 03:03:56.025
2025-07-01 03:03:56.030 >>> d = Differ()
2025-07-01 03:03:56.034 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:56.039 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:56.043 >>> print(''.join(results), end="")
2025-07-01 03:03:56.048 - abcDefghiJkl
2025-07-01 03:03:56.059 + abcdefGhijkl
2025-07-01 03:03:56.068 """
2025-07-01 03:03:56.072
2025-07-01 03:03:56.077 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:56.082 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:56.087 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:56.093 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:56.097 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:56.102
2025-07-01 03:03:56.107 # search for the pair that matches best without being identical
2025-07-01 03:03:56.111 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:56.116 # on junk -- unless we have to)
2025-07-01 03:03:56.120 for j in range(blo, bhi):
2025-07-01 03:03:56.125 bj = b[j]
2025-07-01 03:03:56.130 cruncher.set_seq2(bj)
2025-07-01 03:03:56.135 for i in range(alo, ahi):
2025-07-01 03:03:56.139 ai = a[i]
2025-07-01 03:03:56.143 if ai == bj:
2025-07-01 03:03:56.148 if eqi is None:
2025-07-01 03:03:56.153 eqi, eqj = i, j
2025-07-01 03:03:56.157 continue
2025-07-01 03:03:56.162 cruncher.set_seq1(ai)
2025-07-01 03:03:56.168 # computing similarity is expensive, so use the quick
2025-07-01 03:03:56.172 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:56.177 # compares by a factor of 3.
2025-07-01 03:03:56.182 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:56.187 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:56.192 # of the computation is cached by cruncher
2025-07-01 03:03:56.196 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:56.202 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:56.207 cruncher.ratio() > best_ratio:
2025-07-01 03:03:56.211 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:56.216 if best_ratio < cutoff:
2025-07-01 03:03:56.221 # no non-identical "pretty close" pair
2025-07-01 03:03:56.225 if eqi is None:
2025-07-01 03:03:56.230 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:56.235 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:56.239 return
2025-07-01 03:03:56.243 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:56.248 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:56.253 else:
2025-07-01 03:03:56.257 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:56.262 eqi = None
2025-07-01 03:03:56.267
2025-07-01 03:03:56.278 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:56.294 # identical
2025-07-01 03:03:56.302
2025-07-01 03:03:56.314 # pump out diffs from before the synch point
2025-07-01 03:03:56.326 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:56.334
2025-07-01 03:03:56.350 # do intraline marking on the synch pair
2025-07-01 03:03:56.358 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:56.366 if eqi is None:
2025-07-01 03:03:56.374 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:56.390 atags = btags = ""
2025-07-01 03:03:56.398 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:56.410 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:56.420 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:56.436 if tag == 'replace':
2025-07-01 03:03:56.450 atags += '^' * la
2025-07-01 03:03:56.456 btags += '^' * lb
2025-07-01 03:03:56.467 elif tag == 'delete':
2025-07-01 03:03:56.479 atags += '-' * la
2025-07-01 03:03:56.487 elif tag == 'insert':
2025-07-01 03:03:56.495 btags += '+' * lb
2025-07-01 03:03:56.508 elif tag == 'equal':
2025-07-01 03:03:56.518 atags += ' ' * la
2025-07-01 03:03:56.527 btags += ' ' * lb
2025-07-01 03:03:56.537 else:
2025-07-01 03:03:56.546 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:56.558 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:56.576 else:
2025-07-01 03:03:56.586 # the synch pair is identical
2025-07-01 03:03:56.597 yield '  ' + aelt
2025-07-01 03:03:56.610
2025-07-01 03:03:56.622 # pump out diffs from after the synch point
2025-07-01 03:03:56.634 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:56.646
2025-07-01 03:03:56.654 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:56.670 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:56.678
2025-07-01 03:03:56.686 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:56.697 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:56.710 alo = 348, ahi = 1101
2025-07-01 03:03:56.726 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:56.738 blo = 348, bhi = 1101
2025-07-01 03:03:56.746
2025-07-01 03:03:56.758 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:56.770 g = []
2025-07-01 03:03:56.778 if alo < ahi:
2025-07-01 03:03:56.790 if blo < bhi:
2025-07-01 03:03:56.797 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:56.810 else:
2025-07-01 03:03:56.818 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:56.830 elif blo < bhi:
2025-07-01 03:03:56.842 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:56.854
2025-07-01 03:03:56.866 >       yield from g
2025-07-01 03:03:56.874
2025-07-01 03:03:56.886 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:56.894 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:56.910
2025-07-01 03:03:56.922 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:56.934 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:56.945 alo = 348, ahi = 1101
2025-07-01 03:03:56.958 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:56.970 blo = 348, bhi = 1101
2025-07-01 03:03:56.986
2025-07-01 03:03:56.998 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:57.014 r"""
2025-07-01 03:03:57.030 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:57.038 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:57.053 synch point, and intraline difference marking is done on the
2025-07-01 03:03:57.070 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:57.081
2025-07-01 03:03:57.090 Example:
2025-07-01 03:03:57.102
2025-07-01 03:03:57.118 >>> d = Differ()
2025-07-01 03:03:57.126 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:57.138 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:57.150 >>> print(''.join(results), end="")
2025-07-01 03:03:57.158 - abcDefghiJkl
2025-07-01 03:03:57.182 + abcdefGhijkl
2025-07-01 03:03:57.202 """
2025-07-01 03:03:57.214
2025-07-01 03:03:57.221 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:57.232 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:57.245 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:57.252 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:57.261 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:57.270
2025-07-01 03:03:57.288 # search for the pair that matches best without being identical
2025-07-01 03:03:57.296 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:57.312 # on junk -- unless we have to)
2025-07-01 03:03:57.322 for j in range(blo, bhi):
2025-07-01 03:03:57.334 bj = b[j]
2025-07-01 03:03:57.346 cruncher.set_seq2(bj)
2025-07-01 03:03:57.356 for i in range(alo, ahi):
2025-07-01 03:03:57.376 ai = a[i]
2025-07-01 03:03:57.386 if ai == bj:
2025-07-01 03:03:57.402 if eqi is None:
2025-07-01 03:03:57.418 eqi, eqj = i, j
2025-07-01 03:03:57.434 continue
2025-07-01 03:03:57.450 cruncher.set_seq1(ai)
2025-07-01 03:03:57.462 # computing similarity is expensive, so use the quick
2025-07-01 03:03:57.482 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:57.500 # compares by a factor of 3.
2025-07-01 03:03:57.514 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:57.525 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:57.537 # of the computation is cached by cruncher
2025-07-01 03:03:57.550 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:57.561 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:57.571 cruncher.ratio() > best_ratio:
2025-07-01 03:03:57.586 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:57.593 if best_ratio < cutoff:
2025-07-01 03:03:57.601 # no non-identical "pretty close" pair
2025-07-01 03:03:57.614 if eqi is None:
2025-07-01 03:03:57.630 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:57.642 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:57.649 return
2025-07-01 03:03:57.662 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:57.674 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:57.682 else:
2025-07-01 03:03:57.694 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:57.702 eqi = None
2025-07-01 03:03:57.714
2025-07-01 03:03:57.723 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:57.737 # identical
2025-07-01 03:03:57.755
2025-07-01 03:03:57.770 # pump out diffs from before the synch point
2025-07-01 03:03:57.786 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:57.801
2025-07-01 03:03:57.810 # do intraline marking on the synch pair
2025-07-01 03:03:57.826 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:57.834 if eqi is None:
2025-07-01 03:03:57.841 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:57.849 atags = btags = ""
2025-07-01 03:03:57.864 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:57.874 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:57.885 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:57.904 if tag == 'replace':
2025-07-01 03:03:57.909 atags += '^' * la
2025-07-01 03:03:57.915 btags += '^' * lb
2025-07-01 03:03:57.921 elif tag == 'delete':
2025-07-01 03:03:57.928 atags += '-' * la
2025-07-01 03:03:57.936 elif tag == 'insert':
2025-07-01 03:03:57.948 btags += '+' * lb
2025-07-01 03:03:57.959 elif tag == 'equal':
2025-07-01 03:03:57.977 atags += ' ' * la
2025-07-01 03:03:57.993 btags += ' ' * lb
2025-07-01 03:03:58.006 else:
2025-07-01 03:03:58.025 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:58.041 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:58.052 else:
2025-07-01 03:03:58.064 # the synch pair is identical
2025-07-01 03:03:58.071 yield '  ' + aelt
2025-07-01 03:03:58.079
2025-07-01 03:03:58.093 # pump out diffs from after the synch point
2025-07-01 03:03:58.110 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:58.122
2025-07-01 03:03:58.132 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:58.150 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:58.157
2025-07-01 03:03:58.170 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:58.182 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:58.190 alo = 349, ahi = 1101
2025-07-01 03:03:58.206 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:58.214 blo = 349, bhi = 1101
2025-07-01 03:03:58.222
2025-07-01 03:03:58.234 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:58.242 g = []
2025-07-01 03:03:58.254 if alo < ahi:
2025-07-01 03:03:58.262 if blo < bhi:
2025-07-01 03:03:58.273 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:58.282 else:
2025-07-01 03:03:58.294 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:58.305 elif blo < bhi:
2025-07-01 03:03:58.314 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:58.327
2025-07-01 03:03:58.342 >       yield from g
2025-07-01 03:03:58.354
2025-07-01 03:03:58.361 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:58.374 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:58.382
2025-07-01 03:03:58.395 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:58.414 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:58.430 alo = 349, ahi = 1101
2025-07-01 03:03:58.453 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:58.470 blo = 349, bhi = 1101
2025-07-01 03:03:58.482
2025-07-01 03:03:58.498 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:58.506 r"""
2025-07-01 03:03:58.516 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:58.529 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:58.546 synch point, and intraline difference marking is done on the
2025-07-01 03:03:58.554 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:58.562
2025-07-01 03:03:58.572 Example:
2025-07-01 03:03:58.586
2025-07-01 03:03:58.594 >>> d = Differ()
2025-07-01 03:03:58.604 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:58.616 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:58.629 >>> print(''.join(results), end="")
2025-07-01 03:03:58.644 - abcDefghiJkl
2025-07-01 03:03:58.664 + abcdefGhijkl
2025-07-01 03:03:58.686 """
2025-07-01 03:03:58.694
2025-07-01 03:03:58.710 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:58.721 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:58.734 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:58.748 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:58.759 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:58.772
2025-07-01 03:03:58.786 # search for the pair that matches best without being identical
2025-07-01 03:03:58.797 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:58.813 # on junk -- unless we have to)
2025-07-01 03:03:58.823 for j in range(blo, bhi):
2025-07-01 03:03:58.834 bj = b[j]
2025-07-01 03:03:58.846 cruncher.set_seq2(bj)
2025-07-01 03:03:58.858 for i in range(alo, ahi):
2025-07-01 03:03:58.867 ai = a[i]
2025-07-01 03:03:58.880 if ai == bj:
2025-07-01 03:03:58.890 if eqi is None:
2025-07-01 03:03:58.904 eqi, eqj = i, j
2025-07-01 03:03:58.914 continue
2025-07-01 03:03:58.926 cruncher.set_seq1(ai)
2025-07-01 03:03:58.934 # computing similarity is expensive, so use the quick
2025-07-01 03:03:58.950 # upper bounds first -- have seen this speed up messy
2025-07-01 03:03:58.958 # compares by a factor of 3.
2025-07-01 03:03:58.970 # note that ratio() is only expensive to compute the first
2025-07-01 03:03:58.981 # time it's called on a sequence pair; the expensive part
2025-07-01 03:03:58.994 # of the computation is cached by cruncher
2025-07-01 03:03:59.002 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:03:59.013 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:03:59.026 cruncher.ratio() > best_ratio:
2025-07-01 03:03:59.038 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:03:59.046 if best_ratio < cutoff:
2025-07-01 03:03:59.058 # no non-identical "pretty close" pair
2025-07-01 03:03:59.066 if eqi is None:
2025-07-01 03:03:59.078 # no identical pair either -- treat it as a straight replace
2025-07-01 03:03:59.094 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:59.106 return
2025-07-01 03:03:59.120 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:03:59.127 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:03:59.133 else:
2025-07-01 03:03:59.137 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:03:59.142 eqi = None
2025-07-01 03:03:59.146
2025-07-01 03:03:59.151 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:03:59.156 # identical
2025-07-01 03:03:59.160
2025-07-01 03:03:59.169 # pump out diffs from before the synch point
2025-07-01 03:03:59.175 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:03:59.181
2025-07-01 03:03:59.187 # do intraline marking on the synch pair
2025-07-01 03:03:59.192 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:03:59.197 if eqi is None:
2025-07-01 03:03:59.203 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:03:59.207 atags = btags = ""
2025-07-01 03:03:59.212 cruncher.set_seqs(aelt, belt)
2025-07-01 03:03:59.218 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:03:59.223 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:03:59.228 if tag == 'replace':
2025-07-01 03:03:59.233 atags += '^' * la
2025-07-01 03:03:59.238 btags += '^' * lb
2025-07-01 03:03:59.243 elif tag == 'delete':
2025-07-01 03:03:59.247 atags += '-' * la
2025-07-01 03:03:59.252 elif tag == 'insert':
2025-07-01 03:03:59.257 btags += '+' * lb
2025-07-01 03:03:59.261 elif tag == 'equal':
2025-07-01 03:03:59.266 atags += ' ' * la
2025-07-01 03:03:59.271 btags += ' ' * lb
2025-07-01 03:03:59.276 else:
2025-07-01 03:03:59.280 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:03:59.285 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:03:59.290 else:
2025-07-01 03:03:59.295 # the synch pair is identical
2025-07-01 03:03:59.300 yield '  ' + aelt
2025-07-01 03:03:59.305
2025-07-01 03:03:59.310 # pump out diffs from after the synch point
2025-07-01 03:03:59.318 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:03:59.325
2025-07-01 03:03:59.330 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:03:59.335 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:59.340
2025-07-01 03:03:59.345 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:59.351 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:59.356 alo = 350, ahi = 1101
2025-07-01 03:03:59.361 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:59.370 blo = 350, bhi = 1101
2025-07-01 03:03:59.382
2025-07-01 03:03:59.402 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:59.407 g = []
2025-07-01 03:03:59.425 if alo < ahi:
2025-07-01 03:03:59.438 if blo < bhi:
2025-07-01 03:03:59.445 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:03:59.461 else:
2025-07-01 03:03:59.473 g = self._dump('-', a, alo, ahi)
2025-07-01 03:03:59.491 elif blo < bhi:
2025-07-01 03:03:59.502 g = self._dump('+', b, blo, bhi)
2025-07-01 03:03:59.516
2025-07-01 03:03:59.528 >       yield from g
2025-07-01 03:03:59.546
2025-07-01 03:03:59.557 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:03:59.570 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:03:59.582
2025-07-01 03:03:59.594 self = <difflib.Differ object at [hex]>
2025-07-01 03:03:59.602 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:03:59.614 alo = 350, ahi = 1101
2025-07-01 03:03:59.626 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:03:59.634 blo = 350, bhi = 1101
2025-07-01 03:03:59.646
2025-07-01 03:03:59.658 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:03:59.666 r"""
2025-07-01 03:03:59.682 When replacing one block of lines with another, search the blocks
2025-07-01 03:03:59.694 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:03:59.702 synch point, and intraline difference marking is done on the
2025-07-01 03:03:59.718 similar pair. Lots of work, but often worth it.
2025-07-01 03:03:59.726
2025-07-01 03:03:59.737 Example:
2025-07-01 03:03:59.752
2025-07-01 03:03:59.762 >>> d = Differ()
2025-07-01 03:03:59.774 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:03:59.786 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:03:59.794 >>> print(''.join(results), end="")
2025-07-01 03:03:59.806 - abcDefghiJkl
2025-07-01 03:03:59.830 + abcdefGhijkl
2025-07-01 03:03:59.850 """
2025-07-01 03:03:59.862
2025-07-01 03:03:59.870 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:03:59.886 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:03:59.894 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:03:59.910 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:03:59.918 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:03:59.934
2025-07-01 03:03:59.942 # search for the pair that matches best without being identical
2025-07-01 03:03:59.957 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:03:59.970 # on junk -- unless we have to)
2025-07-01 03:03:59.986 for j in range(blo, bhi):
2025-07-01 03:04:00.002 bj = b[j]
2025-07-01 03:04:00.010 cruncher.set_seq2(bj)
2025-07-01 03:04:00.026 for i in range(alo, ahi):
2025-07-01 03:04:00.035 ai = a[i]
2025-07-01 03:04:00.054 if ai == bj:
2025-07-01 03:04:00.062 if eqi is None:
2025-07-01 03:04:00.077 eqi, eqj = i, j
2025-07-01 03:04:00.090 continue
2025-07-01 03:04:00.098 cruncher.set_seq1(ai)
2025-07-01 03:04:00.110 # computing similarity is expensive, so use the quick
2025-07-01 03:04:00.118 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:00.134 # compares by a factor of 3.
2025-07-01 03:04:00.154 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:00.166 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:00.186 # of the computation is cached by cruncher
2025-07-01 03:04:00.206 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:00.220 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:00.234 cruncher.ratio() > best_ratio:
2025-07-01 03:04:00.243 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:00.258 if best_ratio < cutoff:
2025-07-01 03:04:00.270 # no non-identical "pretty close" pair
2025-07-01 03:04:00.286 if eqi is None:
2025-07-01 03:04:00.302 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:00.314 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:00.324 return
2025-07-01 03:04:00.332 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:00.338 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:00.343 else:
2025-07-01 03:04:00.352 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:00.363 eqi = None
2025-07-01 03:04:00.383
2025-07-01 03:04:00.401 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:00.411 # identical
2025-07-01 03:04:00.422
2025-07-01 03:04:00.431 # pump out diffs from before the synch point
2025-07-01 03:04:00.441 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:00.453
2025-07-01 03:04:00.465 # do intraline marking on the synch pair
2025-07-01 03:04:00.477 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:00.493 if eqi is None:
2025-07-01 03:04:00.505 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:00.511 atags = btags = ""
2025-07-01 03:04:00.525 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:00.535 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:00.548 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:00.557 if tag == 'replace':
2025-07-01 03:04:00.565 atags += '^' * la
2025-07-01 03:04:00.572 btags += '^' * lb
2025-07-01 03:04:00.579 elif tag == 'delete':
2025-07-01 03:04:00.585 atags += '-' * la
2025-07-01 03:04:00.592 elif tag == 'insert':
2025-07-01 03:04:00.598 btags += '+' * lb
2025-07-01 03:04:00.603 elif tag == 'equal':
2025-07-01 03:04:00.609 atags += ' ' * la
2025-07-01 03:04:00.615 btags += ' ' * lb
2025-07-01 03:04:00.620 else:
2025-07-01 03:04:00.626 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:00.632 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:00.638 else:
2025-07-01 03:04:00.643 # the synch pair is identical
2025-07-01 03:04:00.649 yield '  ' + aelt
2025-07-01 03:04:00.655
2025-07-01 03:04:00.661 # pump out diffs from after the synch point
2025-07-01 03:04:00.666 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:00.675
2025-07-01 03:04:00.683 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:00.694 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:00.699
2025-07-01 03:04:00.703 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:00.709 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:00.715 alo = 351, ahi = 1101
2025-07-01 03:04:00.730 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:00.738 blo = 351, bhi = 1101
2025-07-01 03:04:00.750
2025-07-01 03:04:00.762 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:00.770 g = []
2025-07-01 03:04:00.782 if alo < ahi:
2025-07-01 03:04:00.790 if blo < bhi:
2025-07-01 03:04:00.802 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:00.810 else:
2025-07-01 03:04:00.822 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:00.834 elif blo < bhi:
2025-07-01 03:04:00.846 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:00.857
2025-07-01 03:04:00.870 >       yield from g
2025-07-01 03:04:00.886
2025-07-01 03:04:00.894 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:00.906 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:00.918
2025-07-01 03:04:00.926 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:00.942 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:00.950 alo = 351, ahi = 1101
2025-07-01 03:04:00.962 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:00.974 blo = 351, bhi = 1101
2025-07-01 03:04:00.982
2025-07-01 03:04:00.994 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:01.002 r"""
2025-07-01 03:04:01.014 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:01.022 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:01.032 synch point, and intraline difference marking is done on the
2025-07-01 03:04:01.046 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:01.054
2025-07-01 03:04:01.062 Example:
2025-07-01 03:04:01.078
2025-07-01 03:04:01.086 >>> d = Differ()
2025-07-01 03:04:01.094 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:01.110 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:01.122 >>> print(''.join(results), end="")
2025-07-01 03:04:01.134 - abcDefghiJkl
2025-07-01 03:04:01.154 + abcdefGhijkl
2025-07-01 03:04:01.174 """
2025-07-01 03:04:01.190
2025-07-01 03:04:01.202 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:01.214 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:01.222 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:01.234 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:01.250 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:01.266
2025-07-01 03:04:01.275 # search for the pair that matches best without being identical
2025-07-01 03:04:01.286 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:01.297 # on junk -- unless we have to)
2025-07-01 03:04:01.308 for j in range(blo, bhi):
2025-07-01 03:04:01.322 bj = b[j]
2025-07-01 03:04:01.330 cruncher.set_seq2(bj)
2025-07-01 03:04:01.338 for i in range(alo, ahi):
2025-07-01 03:04:01.350 ai = a[i]
2025-07-01 03:04:01.362 if ai == bj:
2025-07-01 03:04:01.374 if eqi is None:
2025-07-01 03:04:01.382 eqi, eqj = i, j
2025-07-01 03:04:01.394 continue
2025-07-01 03:04:01.406 cruncher.set_seq1(ai)
2025-07-01 03:04:01.426 # computing similarity is expensive, so use the quick
2025-07-01 03:04:01.438 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:01.450 # compares by a factor of 3.
2025-07-01 03:04:01.459 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:01.469 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:01.479 # of the computation is cached by cruncher
2025-07-01 03:04:01.491 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:01.509 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:01.517 cruncher.ratio() > best_ratio:
2025-07-01 03:04:01.530 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:01.542 if best_ratio < cutoff:
2025-07-01 03:04:01.557 # no non-identical "pretty close" pair
2025-07-01 03:04:01.569 if eqi is None:
2025-07-01 03:04:01.581 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:01.594 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:01.609 return
2025-07-01 03:04:01.622 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:01.633 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:01.646 else:
2025-07-01 03:04:01.658 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:01.663 eqi = None
2025-07-01 03:04:01.667
2025-07-01 03:04:01.672 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:01.676 # identical
2025-07-01 03:04:01.681
2025-07-01 03:04:01.686 # pump out diffs from before the synch point
2025-07-01 03:04:01.692 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:01.697
2025-07-01 03:04:01.702 # do intraline marking on the synch pair
2025-07-01 03:04:01.707 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:01.713 if eqi is None:
2025-07-01 03:04:01.718 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:01.723 atags = btags = ""
2025-07-01 03:04:01.727 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:01.732 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:01.737 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:01.741 if tag == 'replace':
2025-07-01 03:04:01.746 atags += '^' * la
2025-07-01 03:04:01.751 btags += '^' * lb
2025-07-01 03:04:01.755 elif tag == 'delete':
2025-07-01 03:04:01.760 atags += '-' * la
2025-07-01 03:04:01.764 elif tag == 'insert':
2025-07-01 03:04:01.769 btags += '+' * lb
2025-07-01 03:04:01.773 elif tag == 'equal':
2025-07-01 03:04:01.778 atags += ' ' * la
2025-07-01 03:04:01.782 btags += ' ' * lb
2025-07-01 03:04:01.787 else:
2025-07-01 03:04:01.791 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:01.796 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:01.800 else:
2025-07-01 03:04:01.805 # the synch pair is identical
2025-07-01 03:04:01.809 yield '  ' + aelt
2025-07-01 03:04:01.814
2025-07-01 03:04:01.818 # pump out diffs from after the synch point
2025-07-01 03:04:01.823 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:01.828
2025-07-01 03:04:01.833 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:01.837 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:01.842
2025-07-01 03:04:01.847 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:01.852 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:01.857 alo = 352, ahi = 1101
2025-07-01 03:04:01.862 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:01.867 blo = 352, bhi = 1101
2025-07-01 03:04:01.871
2025-07-01 03:04:01.876 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:01.880 g = []
2025-07-01 03:04:01.885 if alo < ahi:
2025-07-01 03:04:01.890 if blo < bhi:
2025-07-01 03:04:01.894 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:01.899 else:
2025-07-01 03:04:01.904 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:01.908 elif blo < bhi:
2025-07-01 03:04:01.913 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:01.917
2025-07-01 03:04:01.921 >       yield from g
2025-07-01 03:04:01.926
2025-07-01 03:04:01.931 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:01.935 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:01.940
2025-07-01 03:04:01.945 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:01.950 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:01.955 alo = 352, ahi = 1101
2025-07-01 03:04:01.961 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:01.967 blo = 352, bhi = 1101
2025-07-01 03:04:01.972
2025-07-01 03:04:01.977 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:01.982 r"""
2025-07-01 03:04:01.988 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:01.995 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:02.001 synch point, and intraline difference marking is done on the
2025-07-01 03:04:02.007 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:02.012
2025-07-01 03:04:02.016 Example:
2025-07-01 03:04:02.021
2025-07-01 03:04:02.026 >>> d = Differ()
2025-07-01 03:04:02.031 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:02.036 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:02.041 >>> print(''.join(results), end="")
2025-07-01 03:04:02.046 - abcDefghiJkl
2025-07-01 03:04:02.056 + abcdefGhijkl
2025-07-01 03:04:02.065 """
2025-07-01 03:04:02.069
2025-07-01 03:04:02.074 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:02.079 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:02.083 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:02.088 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:02.093 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:02.097
2025-07-01 03:04:02.102 # search for the pair that matches best without being identical
2025-07-01 03:04:02.107 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:02.112 # on junk -- unless we have to)
2025-07-01 03:04:02.117 for j in range(blo, bhi):
2025-07-01 03:04:02.122 bj = b[j]
2025-07-01 03:04:02.126 cruncher.set_seq2(bj)
2025-07-01 03:04:02.131 for i in range(alo, ahi):
2025-07-01 03:04:02.136 ai = a[i]
2025-07-01 03:04:02.141 if ai == bj:
2025-07-01 03:04:02.146 if eqi is None:
2025-07-01 03:04:02.152 eqi, eqj = i, j
2025-07-01 03:04:02.157 continue
2025-07-01 03:04:02.162 cruncher.set_seq1(ai)
2025-07-01 03:04:02.166 # computing similarity is expensive, so use the quick
2025-07-01 03:04:02.171 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:02.176 # compares by a factor of 3.
2025-07-01 03:04:02.180 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:02.185 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:02.190 # of the computation is cached by cruncher
2025-07-01 03:04:02.195 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:02.199 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:02.204 cruncher.ratio() > best_ratio:
2025-07-01 03:04:02.209 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:02.214 if best_ratio < cutoff:
2025-07-01 03:04:02.219 # no non-identical "pretty close" pair
2025-07-01 03:04:02.224 if eqi is None:
2025-07-01 03:04:02.229 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:02.234 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:02.239 return
2025-07-01 03:04:02.243 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:02.248 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:02.253 else:
2025-07-01 03:04:02.258 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:02.262 eqi = None
2025-07-01 03:04:02.267
2025-07-01 03:04:02.272 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:02.276 # identical
2025-07-01 03:04:02.281
2025-07-01 03:04:02.286 # pump out diffs from before the synch point
2025-07-01 03:04:02.291 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:02.295
2025-07-01 03:04:02.301 # do intraline marking on the synch pair
2025-07-01 03:04:02.305 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:02.310 if eqi is None:
2025-07-01 03:04:02.316 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:02.321 atags = btags = ""
2025-07-01 03:04:02.325 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:02.330 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:02.335 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:02.339 if tag == 'replace':
2025-07-01 03:04:02.344 atags += '^' * la
2025-07-01 03:04:02.348 btags += '^' * lb
2025-07-01 03:04:02.353 elif tag == 'delete':
2025-07-01 03:04:02.358 atags += '-' * la
2025-07-01 03:04:02.363 elif tag == 'insert':
2025-07-01 03:04:02.368 btags += '+' * lb
2025-07-01 03:04:02.373 elif tag == 'equal':
2025-07-01 03:04:02.378 atags += ' ' * la
2025-07-01 03:04:02.383 btags += ' ' * lb
2025-07-01 03:04:02.388 else:
2025-07-01 03:04:02.394 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:02.400 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:02.405 else:
2025-07-01 03:04:02.410 # the synch pair is identical
2025-07-01 03:04:02.414 yield '  ' + aelt
2025-07-01 03:04:02.419
2025-07-01 03:04:02.425 # pump out diffs from after the synch point
2025-07-01 03:04:02.431 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:02.435
2025-07-01 03:04:02.441 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:02.446 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:02.450
2025-07-01 03:04:02.455 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:02.461 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:02.466 alo = 353, ahi = 1101
2025-07-01 03:04:02.472 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:02.478 blo = 353, bhi = 1101
2025-07-01 03:04:02.483
2025-07-01 03:04:02.489 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:02.494 g = []
2025-07-01 03:04:02.501 if alo < ahi:
2025-07-01 03:04:02.507 if blo < bhi:
2025-07-01 03:04:02.511 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:02.516 else:
2025-07-01 03:04:02.521 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:02.525 elif blo < bhi:
2025-07-01 03:04:02.530 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:02.535
2025-07-01 03:04:02.539 >       yield from g
2025-07-01 03:04:02.543
2025-07-01 03:04:02.548 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:02.552 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:02.557
2025-07-01 03:04:02.562 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:02.567 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:02.572 alo = 353, ahi = 1101
2025-07-01 03:04:02.577 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:02.582 blo = 353, bhi = 1101
2025-07-01 03:04:02.586
2025-07-01 03:04:02.591 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:02.596 r"""
2025-07-01 03:04:02.601 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:02.605 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:02.610 synch point, and intraline difference marking is done on the
2025-07-01 03:04:02.615 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:02.620
2025-07-01 03:04:02.625 Example:
2025-07-01 03:04:02.629
2025-07-01 03:04:02.634 >>> d = Differ()
2025-07-01 03:04:02.638 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:02.643 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:02.649 >>> print(''.join(results), end="")
2025-07-01 03:04:02.653 - abcDefghiJkl
2025-07-01 03:04:02.662 + abcdefGhijkl
2025-07-01 03:04:02.671 """
2025-07-01 03:04:02.675
2025-07-01 03:04:02.680 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:02.685 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:02.690 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:02.694 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:02.699 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:02.704
2025-07-01 03:04:02.708 # search for the pair that matches best without being identical
2025-07-01 03:04:02.713 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:02.717 # on junk -- unless we have to)
2025-07-01 03:04:02.722 for j in range(blo, bhi):
2025-07-01 03:04:02.726 bj = b[j]
2025-07-01 03:04:02.731 cruncher.set_seq2(bj)
2025-07-01 03:04:02.736 for i in range(alo, ahi):
2025-07-01 03:04:02.741 ai = a[i]
2025-07-01 03:04:02.745 if ai == bj:
2025-07-01 03:04:02.750 if eqi is None:
2025-07-01 03:04:02.754 eqi, eqj = i, j
2025-07-01 03:04:02.759 continue
2025-07-01 03:04:02.764 cruncher.set_seq1(ai)
2025-07-01 03:04:02.768 # computing similarity is expensive, so use the quick
2025-07-01 03:04:02.773 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:02.777 # compares by a factor of 3.
2025-07-01 03:04:02.782 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:02.786 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:02.791 # of the computation is cached by cruncher
2025-07-01 03:04:02.795 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:02.800 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:02.804 cruncher.ratio() > best_ratio:
2025-07-01 03:04:02.809 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:02.813 if best_ratio < cutoff:
2025-07-01 03:04:02.818 # no non-identical "pretty close" pair
2025-07-01 03:04:02.822 if eqi is None:
2025-07-01 03:04:02.827 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:02.831 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:02.836 return
2025-07-01 03:04:02.840 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:02.844 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:02.849 else:
2025-07-01 03:04:02.853 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:02.858 eqi = None
2025-07-01 03:04:02.862
2025-07-01 03:04:02.866 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:02.871 # identical
2025-07-01 03:04:02.875
2025-07-01 03:04:02.879 # pump out diffs from before the synch point
2025-07-01 03:04:02.884 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:02.889
2025-07-01 03:04:02.894 # do intraline marking on the synch pair
2025-07-01 03:04:02.898 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:02.903 if eqi is None:
2025-07-01 03:04:02.907 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:02.912 atags = btags = ""
2025-07-01 03:04:02.916 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:02.921 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:02.925 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:02.929 if tag == 'replace':
2025-07-01 03:04:02.934 atags += '^' * la
2025-07-01 03:04:02.938 btags += '^' * lb
2025-07-01 03:04:02.943 elif tag == 'delete':
2025-07-01 03:04:02.947 atags += '-' * la
2025-07-01 03:04:02.951 elif tag == 'insert':
2025-07-01 03:04:02.956 btags += '+' * lb
2025-07-01 03:04:02.961 elif tag == 'equal':
2025-07-01 03:04:02.965 atags += ' ' * la
2025-07-01 03:04:02.970 btags += ' ' * lb
2025-07-01 03:04:02.974 else:
2025-07-01 03:04:02.979 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:02.983 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:02.988 else:
2025-07-01 03:04:02.992 # the synch pair is identical
2025-07-01 03:04:02.997 yield '  ' + aelt
2025-07-01 03:04:03.002
2025-07-01 03:04:03.007 # pump out diffs from after the synch point
2025-07-01 03:04:03.011 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:03.016
2025-07-01 03:04:03.021 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:03.026 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:03.030
2025-07-01 03:04:03.035 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:03.039 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:03.044 alo = 356, ahi = 1101
2025-07-01 03:04:03.049 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:03.053 blo = 356, bhi = 1101
2025-07-01 03:04:03.058
2025-07-01 03:04:03.062 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:03.067 g = []
2025-07-01 03:04:03.071 if alo < ahi:
2025-07-01 03:04:03.076 if blo < bhi:
2025-07-01 03:04:03.080 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:03.085 else:
2025-07-01 03:04:03.090 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:03.095 elif blo < bhi:
2025-07-01 03:04:03.100 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:03.104
2025-07-01 03:04:03.109 >       yield from g
2025-07-01 03:04:03.113
2025-07-01 03:04:03.118 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:03.123 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:03.127
2025-07-01 03:04:03.131 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:03.136 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:03.141 alo = 356, ahi = 1101
2025-07-01 03:04:03.146 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:03.150 blo = 356, bhi = 1101
2025-07-01 03:04:03.154
2025-07-01 03:04:03.159 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:03.163 r"""
2025-07-01 03:04:03.168 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:03.173 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:03.177 synch point, and intraline difference marking is done on the
2025-07-01 03:04:03.182 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:03.186
2025-07-01 03:04:03.191 Example:
2025-07-01 03:04:03.196
2025-07-01 03:04:03.201 >>> d = Differ()
2025-07-01 03:04:03.206 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:03.210 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:03.215 >>> print(''.join(results), end="")
2025-07-01 03:04:03.219 - abcDefghiJkl
2025-07-01 03:04:03.228 + abcdefGhijkl
2025-07-01 03:04:03.237 """
2025-07-01 03:04:03.241
2025-07-01 03:04:03.245 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:03.250 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:03.255 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:03.259 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:03.263 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:03.268
2025-07-01 03:04:03.273 # search for the pair that matches best without being identical
2025-07-01 03:04:03.279 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:03.286 # on junk -- unless we have to)
2025-07-01 03:04:03.293 for j in range(blo, bhi):
2025-07-01 03:04:03.300 bj = b[j]
2025-07-01 03:04:03.307 cruncher.set_seq2(bj)
2025-07-01 03:04:03.314 for i in range(alo, ahi):
2025-07-01 03:04:03.320 ai = a[i]
2025-07-01 03:04:03.326 if ai == bj:
2025-07-01 03:04:03.332 if eqi is None:
2025-07-01 03:04:03.338 eqi, eqj = i, j
2025-07-01 03:04:03.343 continue
2025-07-01 03:04:03.349 cruncher.set_seq1(ai)
2025-07-01 03:04:03.354 # computing similarity is expensive, so use the quick
2025-07-01 03:04:03.359 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:03.364 # compares by a factor of 3.
2025-07-01 03:04:03.369 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:03.374 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:03.379 # of the computation is cached by cruncher
2025-07-01 03:04:03.384 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:03.389 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:03.394 cruncher.ratio() > best_ratio:
2025-07-01 03:04:03.398 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:03.404 if best_ratio < cutoff:
2025-07-01 03:04:03.409 # no non-identical "pretty close" pair
2025-07-01 03:04:03.415 if eqi is None:
2025-07-01 03:04:03.421 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:03.426 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:03.431 return
2025-07-01 03:04:03.436 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:03.440 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:03.445 else:
2025-07-01 03:04:03.449 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:03.453 eqi = None
2025-07-01 03:04:03.458
2025-07-01 03:04:03.462 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:03.467 # identical
2025-07-01 03:04:03.471
2025-07-01 03:04:03.476 # pump out diffs from before the synch point
2025-07-01 03:04:03.480 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:03.485
2025-07-01 03:04:03.489 # do intraline marking on the synch pair
2025-07-01 03:04:03.493 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:03.498 if eqi is None:
2025-07-01 03:04:03.502 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:03.507 atags = btags = ""
2025-07-01 03:04:03.511 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:03.516 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:03.520 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:03.525 if tag == 'replace':
2025-07-01 03:04:03.530 atags += '^' * la
2025-07-01 03:04:03.535 btags += '^' * lb
2025-07-01 03:04:03.539 elif tag == 'delete':
2025-07-01 03:04:03.545 atags += '-' * la
2025-07-01 03:04:03.550 elif tag == 'insert':
2025-07-01 03:04:03.554 btags += '+' * lb
2025-07-01 03:04:03.559 elif tag == 'equal':
2025-07-01 03:04:03.563 atags += ' ' * la
2025-07-01 03:04:03.568 btags += ' ' * lb
2025-07-01 03:04:03.572 else:
2025-07-01 03:04:03.576 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:03.581 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:03.585 else:
2025-07-01 03:04:03.590 # the synch pair is identical
2025-07-01 03:04:03.594 yield '  ' + aelt
2025-07-01 03:04:03.599
2025-07-01 03:04:03.603 # pump out diffs from after the synch point
2025-07-01 03:04:03.608 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:03.612
2025-07-01 03:04:03.617 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:03.622 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:03.627
2025-07-01 03:04:03.631 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:03.636 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:03.641 alo = 357, ahi = 1101
2025-07-01 03:04:03.645 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:03.650 blo = 357, bhi = 1101
2025-07-01 03:04:03.654
2025-07-01 03:04:03.659 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:03.664 g = []
2025-07-01 03:04:03.670 if alo < ahi:
2025-07-01 03:04:03.676 if blo < bhi:
2025-07-01 03:04:03.682 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:03.688 else:
2025-07-01 03:04:03.693 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:03.698 elif blo < bhi:
2025-07-01 03:04:03.703 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:03.707
2025-07-01 03:04:03.712 >       yield from g
2025-07-01 03:04:03.716
2025-07-01 03:04:03.721 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:03.725 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:03.730
2025-07-01 03:04:03.734 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:03.739 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:03.743 alo = 357, ahi = 1101
2025-07-01 03:04:03.748 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:03.753 blo = 357, bhi = 1101
2025-07-01 03:04:03.757
2025-07-01 03:04:03.762 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:03.766 r"""
2025-07-01 03:04:03.771 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:03.775 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:03.780 synch point, and intraline difference marking is done on the
2025-07-01 03:04:03.784 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:03.790
2025-07-01 03:04:03.795 Example:
2025-07-01 03:04:03.799
2025-07-01 03:04:03.805 >>> d = Differ()
2025-07-01 03:04:03.811 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:03.817 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:03.823 >>> print(''.join(results), end="")
2025-07-01 03:04:03.829 - abcDefghiJkl
2025-07-01 03:04:03.842 + abcdefGhijkl
2025-07-01 03:04:03.851 """
2025-07-01 03:04:03.855
2025-07-01 03:04:03.860 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:03.866 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:03.872 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:03.876 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:03.881 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:03.885
2025-07-01 03:04:03.890 # search for the pair that matches best without being identical
2025-07-01 03:04:03.895 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:03.899 # on junk -- unless we have to)
2025-07-01 03:04:03.905 for j in range(blo, bhi):
2025-07-01 03:04:03.920 bj = b[j]
2025-07-01 03:04:03.934 cruncher.set_seq2(bj)
2025-07-01 03:04:03.946 for i in range(alo, ahi):
2025-07-01 03:04:03.962 ai = a[i]
2025-07-01 03:04:03.978 if ai == bj:
2025-07-01 03:04:03.990 if eqi is None:
2025-07-01 03:04:03.998 eqi, eqj = i, j
2025-07-01 03:04:04.010 continue
2025-07-01 03:04:04.026 cruncher.set_seq1(ai)
2025-07-01 03:04:04.034 # computing similarity is expensive, so use the quick
2025-07-01 03:04:04.042 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:04.058 # compares by a factor of 3.
2025-07-01 03:04:04.068 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:04.078 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:04.087 # of the computation is cached by cruncher
2025-07-01 03:04:04.106 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:04.114 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:04.126 cruncher.ratio() > best_ratio:
2025-07-01 03:04:04.134 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:04.141 if best_ratio < cutoff:
2025-07-01 03:04:04.154 # no non-identical "pretty close" pair
2025-07-01 03:04:04.162 if eqi is None:
2025-07-01 03:04:04.174 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:04.183 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:04.198 return
2025-07-01 03:04:04.211 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:04.223 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:04.234 else:
2025-07-01 03:04:04.246 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:04.252 eqi = None
2025-07-01 03:04:04.265
2025-07-01 03:04:04.276 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:04.289 # identical
2025-07-01 03:04:04.305
2025-07-01 03:04:04.317 # pump out diffs from before the synch point
2025-07-01 03:04:04.334 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:04.349
2025-07-01 03:04:04.358 # do intraline marking on the synch pair
2025-07-01 03:04:04.370 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:04.382 if eqi is None:
2025-07-01 03:04:04.390 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:04.402 atags = btags = ""
2025-07-01 03:04:04.410 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:04.422 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:04.434 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:04.442 if tag == 'replace':
2025-07-01 03:04:04.458 atags += '^' * la
2025-07-01 03:04:04.466 btags += '^' * lb
2025-07-01 03:04:04.475 elif tag == 'delete':
2025-07-01 03:04:04.494 atags += '-' * la
2025-07-01 03:04:04.502 elif tag == 'insert':
2025-07-01 03:04:04.517 btags += '+' * lb
2025-07-01 03:04:04.527 elif tag == 'equal':
2025-07-01 03:04:04.535 atags += ' ' * la
2025-07-01 03:04:04.554 btags += ' ' * lb
2025-07-01 03:04:04.568 else:
2025-07-01 03:04:04.574 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:04.590 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:04.598 else:
2025-07-01 03:04:04.610 # the synch pair is identical
2025-07-01 03:04:04.626 yield '  ' + aelt
2025-07-01 03:04:04.634
2025-07-01 03:04:04.646 # pump out diffs from after the synch point
2025-07-01 03:04:04.658 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:04.666
2025-07-01 03:04:04.682 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:04.693 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:04.706
2025-07-01 03:04:04.714 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:04.726 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:04.738 alo = 358, ahi = 1101
2025-07-01 03:04:04.750 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:04.766 blo = 358, bhi = 1101
2025-07-01 03:04:04.778
2025-07-01 03:04:04.794 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:04.802 g = []
2025-07-01 03:04:04.812 if alo < ahi:
2025-07-01 03:04:04.824 if blo < bhi:
2025-07-01 03:04:04.838 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:04.850 else:
2025-07-01 03:04:04.862 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:04.874 elif blo < bhi:
2025-07-01 03:04:04.882 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:04.894
2025-07-01 03:04:04.906 >       yield from g
2025-07-01 03:04:04.921
2025-07-01 03:04:04.930 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:04.945 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:04.954
2025-07-01 03:04:04.966 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:04.982 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:04.990 alo = 358, ahi = 1101
2025-07-01 03:04:05.003 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:05.014 blo = 358, bhi = 1101
2025-07-01 03:04:05.022
2025-07-01 03:04:05.031 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:05.050 r"""
2025-07-01 03:04:05.066 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:05.074 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:05.095 synch point, and intraline difference marking is done on the
2025-07-01 03:04:05.106 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:05.119
2025-07-01 03:04:05.126 Example:
2025-07-01 03:04:05.138
2025-07-01 03:04:05.150 >>> d = Differ()
2025-07-01 03:04:05.158 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:05.170 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:05.186 >>> print(''.join(results), end="")
2025-07-01 03:04:05.198 - abcDefghiJkl
2025-07-01 03:04:05.218 + abcdefGhijkl
2025-07-01 03:04:05.250 """
2025-07-01 03:04:05.262
2025-07-01 03:04:05.278 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:05.294 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:05.310 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:05.322 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:05.334 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:05.346
2025-07-01 03:04:05.362 # search for the pair that matches best without being identical
2025-07-01 03:04:05.378 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:05.394 # on junk -- unless we have to)
2025-07-01 03:04:05.410 for j in range(blo, bhi):
2025-07-01 03:04:05.422 bj = b[j]
2025-07-01 03:04:05.430 cruncher.set_seq2(bj)
2025-07-01 03:04:05.442 for i in range(alo, ahi):
2025-07-01 03:04:05.450 ai = a[i]
2025-07-01 03:04:05.465 if ai == bj:
2025-07-01 03:04:05.478 if eqi is None:
2025-07-01 03:04:05.486 eqi, eqj = i, j
2025-07-01 03:04:05.498 continue
2025-07-01 03:04:05.510 cruncher.set_seq1(ai)
2025-07-01 03:04:05.530 # computing similarity is expensive, so use the quick
2025-07-01 03:04:05.538 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:05.546 # compares by a factor of 3.
2025-07-01 03:04:05.562 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:05.574 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:05.584 # of the computation is cached by cruncher
2025-07-01 03:04:05.596 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:05.614 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:05.626 cruncher.ratio() > best_ratio:
2025-07-01 03:04:05.640 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:05.650 if best_ratio < cutoff:
2025-07-01 03:04:05.666 # no non-identical "pretty close" pair
2025-07-01 03:04:05.674 if eqi is None:
2025-07-01 03:04:05.686 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:05.699 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:05.714 return
2025-07-01 03:04:05.730 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:05.738 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:05.747 else:
2025-07-01 03:04:05.762 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:05.770 eqi = None
2025-07-01 03:04:05.778
2025-07-01 03:04:05.790 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:05.802 # identical
2025-07-01 03:04:05.810
2025-07-01 03:04:05.822 # pump out diffs from before the synch point
2025-07-01 03:04:05.830 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:05.842
2025-07-01 03:04:05.854 # do intraline marking on the synch pair
2025-07-01 03:04:05.864 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:05.872 if eqi is None:
2025-07-01 03:04:05.881 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:05.890 atags = btags = ""
2025-07-01 03:04:05.899 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:05.914 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:05.929 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:05.941 if tag == 'replace':
2025-07-01 03:04:05.953 atags += '^' * la
2025-07-01 03:04:05.969 btags += '^' * lb
2025-07-01 03:04:05.981 elif tag == 'delete':
2025-07-01 03:04:05.991 atags += '-' * la
2025-07-01 03:04:06.001 elif tag == 'insert':
2025-07-01 03:04:06.016 btags += '+' * lb
2025-07-01 03:04:06.026 elif tag == 'equal':
2025-07-01 03:04:06.040 atags += ' ' * la
2025-07-01 03:04:06.050 btags += ' ' * lb
2025-07-01 03:04:06.068 else:
2025-07-01 03:04:06.082 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:06.094 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:06.106 else:
2025-07-01 03:04:06.122 # the synch pair is identical
2025-07-01 03:04:06.134 yield '  ' + aelt
2025-07-01 03:04:06.146
2025-07-01 03:04:06.158 # pump out diffs from after the synch point
2025-07-01 03:04:06.170 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:06.186
2025-07-01 03:04:06.194 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:06.206 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:06.214
2025-07-01 03:04:06.226 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:06.242 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:06.254 alo = 359, ahi = 1101
2025-07-01 03:04:06.262 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:06.278 blo = 359, bhi = 1101
2025-07-01 03:04:06.287
2025-07-01 03:04:06.302 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:06.309 g = []
2025-07-01 03:04:06.322 if alo < ahi:
2025-07-01 03:04:06.334 if blo < bhi:
2025-07-01 03:04:06.346 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:06.357 else:
2025-07-01 03:04:06.370 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:06.378 elif blo < bhi:
2025-07-01 03:04:06.394 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:06.406
2025-07-01 03:04:06.414 >       yield from g
2025-07-01 03:04:06.422
2025-07-01 03:04:06.429 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:06.442 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:06.450
2025-07-01 03:04:06.458 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:06.473 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:06.486 alo = 359, ahi = 1101
2025-07-01 03:04:06.494 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:06.506 blo = 359, bhi = 1101
2025-07-01 03:04:06.513
2025-07-01 03:04:06.530 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:06.550 r"""
2025-07-01 03:04:06.562 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:06.578 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:06.586 synch point, and intraline difference marking is done on the
2025-07-01 03:04:06.594 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:06.602
2025-07-01 03:04:06.614 Example:
2025-07-01 03:04:06.626
2025-07-01 03:04:06.642 >>> d = Differ()
2025-07-01 03:04:06.658 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:06.670 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:06.678 >>> print(''.join(results), end="")
2025-07-01 03:04:06.690 - abcDefghiJkl
2025-07-01 03:04:06.718 + abcdefGhijkl
2025-07-01 03:04:06.744 """
2025-07-01 03:04:06.758
2025-07-01 03:04:06.770 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:06.782 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:06.794 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:06.806 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:06.813 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:06.821
2025-07-01 03:04:06.829 # search for the pair that matches best without being identical
2025-07-01 03:04:06.834 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:06.839 # on junk -- unless we have to)
2025-07-01 03:04:06.845 for j in range(blo, bhi):
2025-07-01 03:04:06.851 bj = b[j]
2025-07-01 03:04:06.857 cruncher.set_seq2(bj)
2025-07-01 03:04:06.863 for i in range(alo, ahi):
2025-07-01 03:04:06.869 ai = a[i]
2025-07-01 03:04:06.875 if ai == bj:
2025-07-01 03:04:06.881 if eqi is None:
2025-07-01 03:04:06.887 eqi, eqj = i, j
2025-07-01 03:04:06.893 continue
2025-07-01 03:04:06.898 cruncher.set_seq1(ai)
2025-07-01 03:04:06.909 # computing similarity is expensive, so use the quick
2025-07-01 03:04:06.914 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:06.921 # compares by a factor of 3.
2025-07-01 03:04:06.938 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:06.950 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:06.966 # of the computation is cached by cruncher
2025-07-01 03:04:06.982 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:06.990 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:07.003 cruncher.ratio() > best_ratio:
2025-07-01 03:04:07.014 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:07.030 if best_ratio < cutoff:
2025-07-01 03:04:07.042 # no non-identical "pretty close" pair
2025-07-01 03:04:07.050 if eqi is None:
2025-07-01 03:04:07.062 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:07.070 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:07.082 return
2025-07-01 03:04:07.090 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:07.110 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:07.118 else:
2025-07-01 03:04:07.130 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:07.138 eqi = None
2025-07-01 03:04:07.150
2025-07-01 03:04:07.158 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:07.170 # identical
2025-07-01 03:04:07.182
2025-07-01 03:04:07.194 # pump out diffs from before the synch point
2025-07-01 03:04:07.206 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:07.218
2025-07-01 03:04:07.230 # do intraline marking on the synch pair
2025-07-01 03:04:07.242 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:07.254 if eqi is None:
2025-07-01 03:04:07.262 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:07.274 atags = btags = ""
2025-07-01 03:04:07.286 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:07.300 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:07.314 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:07.326 if tag == 'replace':
2025-07-01 03:04:07.338 atags += '^' * la
2025-07-01 03:04:07.350 btags += '^' * lb
2025-07-01 03:04:07.358 elif tag == 'delete':
2025-07-01 03:04:07.370 atags += '-' * la
2025-07-01 03:04:07.378 elif tag == 'insert':
2025-07-01 03:04:07.390 btags += '+' * lb
2025-07-01 03:04:07.405 elif tag == 'equal':
2025-07-01 03:04:07.414 atags += ' ' * la
2025-07-01 03:04:07.426 btags += ' ' * lb
2025-07-01 03:04:07.442 else:
2025-07-01 03:04:07.450 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:07.466 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:07.474 else:
2025-07-01 03:04:07.490 # the synch pair is identical
2025-07-01 03:04:07.498 yield '  ' + aelt
2025-07-01 03:04:07.506
2025-07-01 03:04:07.519 # pump out diffs from after the synch point
2025-07-01 03:04:07.534 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:07.542
2025-07-01 03:04:07.555 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:07.570 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:07.577
2025-07-01 03:04:07.590 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:07.606 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:07.617 alo = 360, ahi = 1101
2025-07-01 03:04:07.630 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:07.645 blo = 360, bhi = 1101
2025-07-01 03:04:07.657
2025-07-01 03:04:07.673 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:07.682 g = []
2025-07-01 03:04:07.698 if alo < ahi:
2025-07-01 03:04:07.708 if blo < bhi:
2025-07-01 03:04:07.721 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:07.738 else:
2025-07-01 03:04:07.746 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:07.758 elif blo < bhi:
2025-07-01 03:04:07.766 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:07.778
2025-07-01 03:04:07.785 >       yield from g
2025-07-01 03:04:07.798
2025-07-01 03:04:07.806 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:07.818 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:07.823
2025-07-01 03:04:07.838 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:07.850 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:07.858 alo = 360, ahi = 1101
2025-07-01 03:04:07.866 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:07.878 blo = 360, bhi = 1101
2025-07-01 03:04:07.886
2025-07-01 03:04:07.898 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:07.910 r"""
2025-07-01 03:04:07.919 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:07.934 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:07.942 synch point, and intraline difference marking is done on the
2025-07-01 03:04:07.954 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:07.962
2025-07-01 03:04:07.974 Example:
2025-07-01 03:04:07.986
2025-07-01 03:04:07.998 >>> d = Differ()
2025-07-01 03:04:08.006 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:08.014 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:08.024 >>> print(''.join(results), end="")
2025-07-01 03:04:08.038 - abcDefghiJkl
2025-07-01 03:04:08.058 + abcdefGhijkl
2025-07-01 03:04:08.078 """
2025-07-01 03:04:08.090
2025-07-01 03:04:08.098 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:08.110 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:08.118 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:08.130 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:08.138 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:08.154
2025-07-01 03:04:08.165 # search for the pair that matches best without being identical
2025-07-01 03:04:08.182 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:08.190 # on junk -- unless we have to)
2025-07-01 03:04:08.198 for j in range(blo, bhi):
2025-07-01 03:04:08.210 bj = b[j]
2025-07-01 03:04:08.219 cruncher.set_seq2(bj)
2025-07-01 03:04:08.230 for i in range(alo, ahi):
2025-07-01 03:04:08.242 ai = a[i]
2025-07-01 03:04:08.249 if ai == bj:
2025-07-01 03:04:08.262 if eqi is None:
2025-07-01 03:04:08.269 eqi, eqj = i, j
2025-07-01 03:04:08.282 continue
2025-07-01 03:04:08.290 cruncher.set_seq1(ai)
2025-07-01 03:04:08.302 # computing similarity is expensive, so use the quick
2025-07-01 03:04:08.314 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:08.319 # compares by a factor of 3.
2025-07-01 03:04:08.334 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:08.342 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:08.354 # of the computation is cached by cruncher
2025-07-01 03:04:08.366 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:08.374 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:08.386 cruncher.ratio() > best_ratio:
2025-07-01 03:04:08.399 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:08.410 if best_ratio < cutoff:
2025-07-01 03:04:08.425 # no non-identical "pretty close" pair
2025-07-01 03:04:08.434 if eqi is None:
2025-07-01 03:04:08.452 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:08.462 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:08.474 return
2025-07-01 03:04:08.486 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:08.498 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:08.510 else:
2025-07-01 03:04:08.522 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:08.534 eqi = None
2025-07-01 03:04:08.550
2025-07-01 03:04:08.558 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:08.566 # identical
2025-07-01 03:04:08.576
2025-07-01 03:04:08.590 # pump out diffs from before the synch point
2025-07-01 03:04:08.602 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:08.613
2025-07-01 03:04:08.626 # do intraline marking on the synch pair
2025-07-01 03:04:08.634 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:08.644 if eqi is None:
2025-07-01 03:04:08.651 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:08.665 atags = btags = ""
2025-07-01 03:04:08.673 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:08.686 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:08.698 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:08.706 if tag == 'replace':
2025-07-01 03:04:08.718 atags += '^' * la
2025-07-01 03:04:08.726 btags += '^' * lb
2025-07-01 03:04:08.737 elif tag == 'delete':
2025-07-01 03:04:08.748 atags += '-' * la
2025-07-01 03:04:08.757 elif tag == 'insert':
2025-07-01 03:04:08.770 btags += '+' * lb
2025-07-01 03:04:08.783 elif tag == 'equal':
2025-07-01 03:04:08.789 atags += ' ' * la
2025-07-01 03:04:08.802 btags += ' ' * lb
2025-07-01 03:04:08.813 else:
2025-07-01 03:04:08.824 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:08.841 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:08.848 else:
2025-07-01 03:04:08.854 # the synch pair is identical
2025-07-01 03:04:08.860 yield '  ' + aelt
2025-07-01 03:04:08.866
2025-07-01 03:04:08.872 # pump out diffs from after the synch point
2025-07-01 03:04:08.878 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:08.883
2025-07-01 03:04:08.889 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:08.896 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:08.903
2025-07-01 03:04:08.910 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:08.919 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:08.930 alo = 361, ahi = 1101
2025-07-01 03:04:08.939 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:08.953 blo = 361, bhi = 1101
2025-07-01 03:04:08.966
2025-07-01 03:04:08.978 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:08.990 g = []
2025-07-01 03:04:09.002 if alo < ahi:
2025-07-01 03:04:09.014 if blo < bhi:
2025-07-01 03:04:09.026 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:09.040 else:
2025-07-01 03:04:09.049 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:09.061 elif blo < bhi:
2025-07-01 03:04:09.074 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:09.081
2025-07-01 03:04:09.094 >       yield from g
2025-07-01 03:04:09.106
2025-07-01 03:04:09.115 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:09.126 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:09.138
2025-07-01 03:04:09.150 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:09.166 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:09.186 alo = 361, ahi = 1101
2025-07-01 03:04:09.198 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:09.206 blo = 361, bhi = 1101
2025-07-01 03:04:09.222
2025-07-01 03:04:09.235 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:09.254 r"""
2025-07-01 03:04:09.266 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:09.274 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:09.286 synch point, and intraline difference marking is done on the
2025-07-01 03:04:09.294 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:09.306
2025-07-01 03:04:09.314 Example:
2025-07-01 03:04:09.326
2025-07-01 03:04:09.334 >>> d = Differ()
2025-07-01 03:04:09.346 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:09.354 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:09.366 >>> print(''.join(results), end="")
2025-07-01 03:04:09.374 - abcDefghiJkl
2025-07-01 03:04:09.394 + abcdefGhijkl
2025-07-01 03:04:09.418 """
2025-07-01 03:04:09.430
2025-07-01 03:04:09.438 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:09.450 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:09.458 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:09.470 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:09.478 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:09.490
2025-07-01 03:04:09.502 # search for the pair that matches best without being identical
2025-07-01 03:04:09.514 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:09.522 # on junk -- unless we have to)
2025-07-01 03:04:09.530 for j in range(blo, bhi):
2025-07-01 03:04:09.542 bj = b[j]
2025-07-01 03:04:09.558 cruncher.set_seq2(bj)
2025-07-01 03:04:09.566 for i in range(alo, ahi):
2025-07-01 03:04:09.578 ai = a[i]
2025-07-01 03:04:09.586 if ai == bj:
2025-07-01 03:04:09.594 if eqi is None:
2025-07-01 03:04:09.610 eqi, eqj = i, j
2025-07-01 03:04:09.618 continue
2025-07-01 03:04:09.634 cruncher.set_seq1(ai)
2025-07-01 03:04:09.642 # computing similarity is expensive, so use the quick
2025-07-01 03:04:09.654 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:09.666 # compares by a factor of 3.
2025-07-01 03:04:09.674 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:09.686 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:09.698 # of the computation is cached by cruncher
2025-07-01 03:04:09.706 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:09.718 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:09.728 cruncher.ratio() > best_ratio:
2025-07-01 03:04:09.738 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:09.752 if best_ratio < cutoff:
2025-07-01 03:04:09.762 # no non-identical "pretty close" pair
2025-07-01 03:04:09.774 if eqi is None:
2025-07-01 03:04:09.786 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:09.793 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:09.806 return
2025-07-01 03:04:09.822 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:09.834 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:09.846 else:
2025-07-01 03:04:09.853 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:09.866 eqi = None
2025-07-01 03:04:09.880
2025-07-01 03:04:09.892 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:09.906 # identical
2025-07-01 03:04:09.918
2025-07-01 03:04:09.926 # pump out diffs from before the synch point
2025-07-01 03:04:09.942 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:09.953
2025-07-01 03:04:09.968 # do intraline marking on the synch pair
2025-07-01 03:04:09.978 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:09.989 if eqi is None:
2025-07-01 03:04:10.002 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:10.011 atags = btags = ""
2025-07-01 03:04:10.023 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:10.038 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:10.046 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:10.058 if tag == 'replace':
2025-07-01 03:04:10.070 atags += '^' * la
2025-07-01 03:04:10.082 btags += '^' * lb
2025-07-01 03:04:10.089 elif tag == 'delete':
2025-07-01 03:04:10.102 atags += '-' * la
2025-07-01 03:04:10.109 elif tag == 'insert':
2025-07-01 03:04:10.122 btags += '+' * lb
2025-07-01 03:04:10.137 elif tag == 'equal':
2025-07-01 03:04:10.146 atags += ' ' * la
2025-07-01 03:04:10.158 btags += ' ' * lb
2025-07-01 03:04:10.166 else:
2025-07-01 03:04:10.174 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:10.190 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:10.197 else:
2025-07-01 03:04:10.210 # the synch pair is identical
2025-07-01 03:04:10.225 yield '  ' + aelt
2025-07-01 03:04:10.234
2025-07-01 03:04:10.246 # pump out diffs from after the synch point
2025-07-01 03:04:10.253 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:10.266
2025-07-01 03:04:10.275 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:10.288 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:10.300
2025-07-01 03:04:10.314 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:10.326 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:10.338 alo = 362, ahi = 1101
2025-07-01 03:04:10.346 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:10.357 blo = 362, bhi = 1101
2025-07-01 03:04:10.374
2025-07-01 03:04:10.379 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:10.386 g = []
2025-07-01 03:04:10.394 if alo < ahi:
2025-07-01 03:04:10.406 if blo < bhi:
2025-07-01 03:04:10.414 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:10.422 else:
2025-07-01 03:04:10.430 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:10.438 elif blo < bhi:
2025-07-01 03:04:10.446 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:10.458
2025-07-01 03:04:10.477 >       yield from g
2025-07-01 03:04:10.494
2025-07-01 03:04:10.516 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:10.530 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:10.542
2025-07-01 03:04:10.558 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:10.566 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:10.575 alo = 362, ahi = 1101
2025-07-01 03:04:10.599 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:10.614 blo = 362, bhi = 1101
2025-07-01 03:04:10.626
2025-07-01 03:04:10.641 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:10.653 r"""
2025-07-01 03:04:10.666 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:10.677 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:10.693 synch point, and intraline difference marking is done on the
2025-07-01 03:04:10.706 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:10.717
2025-07-01 03:04:10.733 Example:
2025-07-01 03:04:10.745
2025-07-01 03:04:10.757 >>> d = Differ()
2025-07-01 03:04:10.770 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:10.785 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:10.797 >>> print(''.join(results), end="")
2025-07-01 03:04:10.810 - abcDefghiJkl
2025-07-01 03:04:10.830 + abcdefGhijkl
2025-07-01 03:04:10.852 """
2025-07-01 03:04:10.870
2025-07-01 03:04:10.882 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:10.894 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:10.906 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:10.914 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:10.928 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:10.942
2025-07-01 03:04:10.958 # search for the pair that matches best without being identical
2025-07-01 03:04:10.972 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:10.982 # on junk -- unless we have to)
2025-07-01 03:04:10.998 for j in range(blo, bhi):
2025-07-01 03:04:11.010 bj = b[j]
2025-07-01 03:04:11.022 cruncher.set_seq2(bj)
2025-07-01 03:04:11.037 for i in range(alo, ahi):
2025-07-01 03:04:11.047 ai = a[i]
2025-07-01 03:04:11.052 if ai == bj:
2025-07-01 03:04:11.056 if eqi is None:
2025-07-01 03:04:11.061 eqi, eqj = i, j
2025-07-01 03:04:11.065 continue
2025-07-01 03:04:11.070 cruncher.set_seq1(ai)
2025-07-01 03:04:11.074 # computing similarity is expensive, so use the quick
2025-07-01 03:04:11.079 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:11.084 # compares by a factor of 3.
2025-07-01 03:04:11.088 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:11.093 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:11.098 # of the computation is cached by cruncher
2025-07-01 03:04:11.103 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:11.107 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:11.112 cruncher.ratio() > best_ratio:
2025-07-01 03:04:11.117 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:11.121 if best_ratio < cutoff:
2025-07-01 03:04:11.126 # no non-identical "pretty close" pair
2025-07-01 03:04:11.130 if eqi is None:
2025-07-01 03:04:11.134 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:11.139 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:11.144 return
2025-07-01 03:04:11.148 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:11.153 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:11.157 else:
2025-07-01 03:04:11.161 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:11.166 eqi = None
2025-07-01 03:04:11.170
2025-07-01 03:04:11.175 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:11.179 # identical
2025-07-01 03:04:11.183
2025-07-01 03:04:11.188 # pump out diffs from before the synch point
2025-07-01 03:04:11.192 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:11.197
2025-07-01 03:04:11.201 # do intraline marking on the synch pair
2025-07-01 03:04:11.206 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:11.210 if eqi is None:
2025-07-01 03:04:11.214 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:11.219 atags = btags = ""
2025-07-01 03:04:11.223 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:11.228 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:11.232 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:11.237 if tag == 'replace':
2025-07-01 03:04:11.241 atags += '^' * la
2025-07-01 03:04:11.246 btags += '^' * lb
2025-07-01 03:04:11.250 elif tag == 'delete':
2025-07-01 03:04:11.254 atags += '-' * la
2025-07-01 03:04:11.259 elif tag == 'insert':
2025-07-01 03:04:11.263 btags += '+' * lb
2025-07-01 03:04:11.268 elif tag == 'equal':
2025-07-01 03:04:11.272 atags += ' ' * la
2025-07-01 03:04:11.276 btags += ' ' * lb
2025-07-01 03:04:11.282 else:
2025-07-01 03:04:11.288 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:11.294 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:11.300 else:
2025-07-01 03:04:11.305 # the synch pair is identical
2025-07-01 03:04:11.310 yield '  ' + aelt
2025-07-01 03:04:11.315
2025-07-01 03:04:11.321 # pump out diffs from after the synch point
2025-07-01 03:04:11.327 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:11.332
2025-07-01 03:04:11.338 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:11.343 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:11.349
2025-07-01 03:04:11.355 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:11.361 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:11.367 alo = 363, ahi = 1101
2025-07-01 03:04:11.373 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:11.378 blo = 363, bhi = 1101
2025-07-01 03:04:11.383
2025-07-01 03:04:11.389 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:11.395 g = []
2025-07-01 03:04:11.400 if alo < ahi:
2025-07-01 03:04:11.406 if blo < bhi:
2025-07-01 03:04:11.410 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:11.415 else:
2025-07-01 03:04:11.420 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:11.425 elif blo < bhi:
2025-07-01 03:04:11.431 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:11.436
2025-07-01 03:04:11.442 >       yield from g
2025-07-01 03:04:11.447
2025-07-01 03:04:11.452 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:11.457 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:11.461
2025-07-01 03:04:11.465 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:11.470 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:11.475 alo = 363, ahi = 1101
2025-07-01 03:04:11.480 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:11.484 blo = 363, bhi = 1101
2025-07-01 03:04:11.488
2025-07-01 03:04:11.493 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:11.497 r"""
2025-07-01 03:04:11.501 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:11.506 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:11.510 synch point, and intraline difference marking is done on the
2025-07-01 03:04:11.515 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:11.519
2025-07-01 03:04:11.523 Example:
2025-07-01 03:04:11.527
2025-07-01 03:04:11.532 >>> d = Differ()
2025-07-01 03:04:11.536 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:11.541 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:11.545 >>> print(''.join(results), end="")
2025-07-01 03:04:11.549 - abcDefghiJkl
2025-07-01 03:04:11.558 + abcdefGhijkl
2025-07-01 03:04:11.566 """
2025-07-01 03:04:11.571
2025-07-01 03:04:11.575 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:11.579 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:11.584 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:11.589 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:11.594 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:11.598
2025-07-01 03:04:11.603 # search for the pair that matches best without being identical
2025-07-01 03:04:11.608 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:11.612 # on junk -- unless we have to)
2025-07-01 03:04:11.617 for j in range(blo, bhi):
2025-07-01 03:04:11.622 bj = b[j]
2025-07-01 03:04:11.626 cruncher.set_seq2(bj)
2025-07-01 03:04:11.631 for i in range(alo, ahi):
2025-07-01 03:04:11.635 ai = a[i]
2025-07-01 03:04:11.640 if ai == bj:
2025-07-01 03:04:11.645 if eqi is None:
2025-07-01 03:04:11.649 eqi, eqj = i, j
2025-07-01 03:04:11.654 continue
2025-07-01 03:04:11.659 cruncher.set_seq1(ai)
2025-07-01 03:04:11.665 # computing similarity is expensive, so use the quick
2025-07-01 03:04:11.671 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:11.676 # compares by a factor of 3.
2025-07-01 03:04:11.682 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:11.688 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:11.694 # of the computation is cached by cruncher
2025-07-01 03:04:11.700 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:11.705 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:11.710 cruncher.ratio() > best_ratio:
2025-07-01 03:04:11.718 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:11.723 if best_ratio < cutoff:
2025-07-01 03:04:11.729 # no non-identical "pretty close" pair
2025-07-01 03:04:11.733 if eqi is None:
2025-07-01 03:04:11.739 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:11.745 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:11.749 return
2025-07-01 03:04:11.754 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:11.759 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:11.764 else:
2025-07-01 03:04:11.770 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:11.775 eqi = None
2025-07-01 03:04:11.781
2025-07-01 03:04:11.786 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:11.792 # identical
2025-07-01 03:04:11.798
2025-07-01 03:04:11.803 # pump out diffs from before the synch point
2025-07-01 03:04:11.809 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:11.814
2025-07-01 03:04:11.820 # do intraline marking on the synch pair
2025-07-01 03:04:11.826 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:11.830 if eqi is None:
2025-07-01 03:04:11.836 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:11.842 atags = btags = ""
2025-07-01 03:04:11.847 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:11.854 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:11.870 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:11.882 if tag == 'replace':
2025-07-01 03:04:11.894 atags += '^' * la
2025-07-01 03:04:11.906 btags += '^' * lb
2025-07-01 03:04:11.918 elif tag == 'delete':
2025-07-01 03:04:11.930 atags += '-' * la
2025-07-01 03:04:11.942 elif tag == 'insert':
2025-07-01 03:04:11.958 btags += '+' * lb
2025-07-01 03:04:11.966 elif tag == 'equal':
2025-07-01 03:04:11.974 atags += ' ' * la
2025-07-01 03:04:11.986 btags += ' ' * lb
2025-07-01 03:04:11.996 else:
2025-07-01 03:04:12.014 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:12.026 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:12.038 else:
2025-07-01 03:04:12.050 # the synch pair is identical
2025-07-01 03:04:12.062 yield '  ' + aelt
2025-07-01 03:04:12.074
2025-07-01 03:04:12.085 # pump out diffs from after the synch point
2025-07-01 03:04:12.102 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:12.113
2025-07-01 03:04:12.122 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:12.130 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:12.151
2025-07-01 03:04:12.170 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:12.182 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:12.194 alo = 364, ahi = 1101
2025-07-01 03:04:12.202 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:12.212 blo = 364, bhi = 1101
2025-07-01 03:04:12.228
2025-07-01 03:04:12.242 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:12.254 g = []
2025-07-01 03:04:12.262 if alo < ahi:
2025-07-01 03:04:12.274 if blo < bhi:
2025-07-01 03:04:12.282 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:12.294 else:
2025-07-01 03:04:12.302 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:12.314 elif blo < bhi:
2025-07-01 03:04:12.325 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:12.340
2025-07-01 03:04:12.350 >       yield from g
2025-07-01 03:04:12.362
2025-07-01 03:04:12.378 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:12.386 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:12.398
2025-07-01 03:04:12.414 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:12.426 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:12.438 alo = 364, ahi = 1101
2025-07-01 03:04:12.454 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:12.466 blo = 364, bhi = 1101
2025-07-01 03:04:12.478
2025-07-01 03:04:12.490 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:12.498 r"""
2025-07-01 03:04:12.510 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:12.517 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:12.532 synch point, and intraline difference marking is done on the
2025-07-01 03:04:12.542 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:12.554
2025-07-01 03:04:12.566 Example:
2025-07-01 03:04:12.574
2025-07-01 03:04:12.586 >>> d = Differ()
2025-07-01 03:04:12.598 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:12.606 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:12.618 >>> print(''.join(results), end="")
2025-07-01 03:04:12.630 - abcDefghiJkl
2025-07-01 03:04:12.658 + abcdefGhijkl
2025-07-01 03:04:12.686 """
2025-07-01 03:04:12.692
2025-07-01 03:04:12.697 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:12.702 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:12.706 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:12.711 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:12.716 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:12.720
2025-07-01 03:04:12.724 # search for the pair that matches best without being identical
2025-07-01 03:04:12.729 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:12.734 # on junk -- unless we have to)
2025-07-01 03:04:12.738 for j in range(blo, bhi):
2025-07-01 03:04:12.742 bj = b[j]
2025-07-01 03:04:12.747 cruncher.set_seq2(bj)
2025-07-01 03:04:12.752 for i in range(alo, ahi):
2025-07-01 03:04:12.756 ai = a[i]
2025-07-01 03:04:12.761 if ai == bj:
2025-07-01 03:04:12.767 if eqi is None:
2025-07-01 03:04:12.771 eqi, eqj = i, j
2025-07-01 03:04:12.776 continue
2025-07-01 03:04:12.780 cruncher.set_seq1(ai)
2025-07-01 03:04:12.784 # computing similarity is expensive, so use the quick
2025-07-01 03:04:12.789 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:12.793 # compares by a factor of 3.
2025-07-01 03:04:12.798 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:12.802 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:12.807 # of the computation is cached by cruncher
2025-07-01 03:04:12.811 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:12.816 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:12.821 cruncher.ratio() > best_ratio:
2025-07-01 03:04:12.825 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:12.830 if best_ratio < cutoff:
2025-07-01 03:04:12.834 # no non-identical "pretty close" pair
2025-07-01 03:04:12.838 if eqi is None:
2025-07-01 03:04:12.843 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:12.848 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:12.852 return
2025-07-01 03:04:12.856 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:12.861 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:12.865 else:
2025-07-01 03:04:12.870 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:12.874 eqi = None
2025-07-01 03:04:12.878
2025-07-01 03:04:12.882 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:12.887 # identical
2025-07-01 03:04:12.891
2025-07-01 03:04:12.895 # pump out diffs from before the synch point
2025-07-01 03:04:12.900 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:12.904
2025-07-01 03:04:12.909 # do intraline marking on the synch pair
2025-07-01 03:04:12.913 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:12.917 if eqi is None:
2025-07-01 03:04:12.922 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:12.926 atags = btags = ""
2025-07-01 03:04:12.930 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:12.935 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:12.939 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:12.943 if tag == 'replace':
2025-07-01 03:04:12.948 atags += '^' * la
2025-07-01 03:04:12.952 btags += '^' * lb
2025-07-01 03:04:12.956 elif tag == 'delete':
2025-07-01 03:04:12.961 atags += '-' * la
2025-07-01 03:04:12.965 elif tag == 'insert':
2025-07-01 03:04:12.969 btags += '+' * lb
2025-07-01 03:04:12.974 elif tag == 'equal':
2025-07-01 03:04:12.978 atags += ' ' * la
2025-07-01 03:04:12.982 btags += ' ' * lb
2025-07-01 03:04:12.986 else:
2025-07-01 03:04:12.991 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:12.995 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:13.000 else:
2025-07-01 03:04:13.004 # the synch pair is identical
2025-07-01 03:04:13.009 yield '  ' + aelt
2025-07-01 03:04:13.013
2025-07-01 03:04:13.018 # pump out diffs from after the synch point
2025-07-01 03:04:13.022 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:13.027
2025-07-01 03:04:13.031 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:13.036 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:13.040
2025-07-01 03:04:13.045 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:13.050 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:13.055 alo = 365, ahi = 1101
2025-07-01 03:04:13.060 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:13.064 blo = 365, bhi = 1101
2025-07-01 03:04:13.069
2025-07-01 03:04:13.073 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:13.078 g = []
2025-07-01 03:04:13.082 if alo < ahi:
2025-07-01 03:04:13.086 if blo < bhi:
2025-07-01 03:04:13.091 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:13.095 else:
2025-07-01 03:04:13.100 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:13.105 elif blo < bhi:
2025-07-01 03:04:13.110 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:13.114
2025-07-01 03:04:13.120 >       yield from g
2025-07-01 03:04:13.125
2025-07-01 03:04:13.131 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:13.137 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:13.142
2025-07-01 03:04:13.146 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:13.153 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:13.158 alo = 365, ahi = 1101
2025-07-01 03:04:13.164 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:13.168 blo = 365, bhi = 1101
2025-07-01 03:04:13.173
2025-07-01 03:04:13.179 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:13.184 r"""
2025-07-01 03:04:13.189 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:13.194 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:13.200 synch point, and intraline difference marking is done on the
2025-07-01 03:04:13.206 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:13.211
2025-07-01 03:04:13.216 Example:
2025-07-01 03:04:13.220
2025-07-01 03:04:13.225 >>> d = Differ()
2025-07-01 03:04:13.230 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:13.236 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:13.242 >>> print(''.join(results), end="")
2025-07-01 03:04:13.247 - abcDefghiJkl
2025-07-01 03:04:13.257 + abcdefGhijkl
2025-07-01 03:04:13.268 """
2025-07-01 03:04:13.273
2025-07-01 03:04:13.278 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:13.283 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:13.288 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:13.294 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:13.299 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:13.303
2025-07-01 03:04:13.308 # search for the pair that matches best without being identical
2025-07-01 03:04:13.314 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:13.319 # on junk -- unless we have to)
2025-07-01 03:04:13.324 for j in range(blo, bhi):
2025-07-01 03:04:13.329 bj = b[j]
2025-07-01 03:04:13.334 cruncher.set_seq2(bj)
2025-07-01 03:04:13.341 for i in range(alo, ahi):
2025-07-01 03:04:13.346 ai = a[i]
2025-07-01 03:04:13.351 if ai == bj:
2025-07-01 03:04:13.355 if eqi is None:
2025-07-01 03:04:13.360 eqi, eqj = i, j
2025-07-01 03:04:13.364 continue
2025-07-01 03:04:13.368 cruncher.set_seq1(ai)
2025-07-01 03:04:13.373 # computing similarity is expensive, so use the quick
2025-07-01 03:04:13.377 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:13.382 # compares by a factor of 3.
2025-07-01 03:04:13.386 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:13.391 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:13.395 # of the computation is cached by cruncher
2025-07-01 03:04:13.399 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:13.404 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:13.408 cruncher.ratio() > best_ratio:
2025-07-01 03:04:13.413 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:13.417 if best_ratio < cutoff:
2025-07-01 03:04:13.421 # no non-identical "pretty close" pair
2025-07-01 03:04:13.426 if eqi is None:
2025-07-01 03:04:13.430 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:13.435 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:13.439 return
2025-07-01 03:04:13.443 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:13.448 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:13.452 else:
2025-07-01 03:04:13.456 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:13.461 eqi = None
2025-07-01 03:04:13.465
2025-07-01 03:04:13.470 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:13.474 # identical
2025-07-01 03:04:13.479
2025-07-01 03:04:13.483 # pump out diffs from before the synch point
2025-07-01 03:04:13.488 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:13.492
2025-07-01 03:04:13.496 # do intraline marking on the synch pair
2025-07-01 03:04:13.501 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:13.505 if eqi is None:
2025-07-01 03:04:13.509 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:13.514 atags = btags = ""
2025-07-01 03:04:13.518 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:13.522 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:13.527 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:13.531 if tag == 'replace':
2025-07-01 03:04:13.535 atags += '^' * la
2025-07-01 03:04:13.540 btags += '^' * lb
2025-07-01 03:04:13.544 elif tag == 'delete':
2025-07-01 03:04:13.548 atags += '-' * la
2025-07-01 03:04:13.552 elif tag == 'insert':
2025-07-01 03:04:13.557 btags += '+' * lb
2025-07-01 03:04:13.561 elif tag == 'equal':
2025-07-01 03:04:13.565 atags += ' ' * la
2025-07-01 03:04:13.570 btags += ' ' * lb
2025-07-01 03:04:13.574 else:
2025-07-01 03:04:13.579 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:13.584 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:13.588 else:
2025-07-01 03:04:13.592 # the synch pair is identical
2025-07-01 03:04:13.597 yield '  ' + aelt
2025-07-01 03:04:13.601
2025-07-01 03:04:13.606 # pump out diffs from after the synch point
2025-07-01 03:04:13.610 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:13.615
2025-07-01 03:04:13.619 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:13.624 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:13.628
2025-07-01 03:04:13.633 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:13.637 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:13.641 alo = 366, ahi = 1101
2025-07-01 03:04:13.646 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:13.651 blo = 366, bhi = 1101
2025-07-01 03:04:13.655
2025-07-01 03:04:13.660 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:13.664 g = []
2025-07-01 03:04:13.669 if alo < ahi:
2025-07-01 03:04:13.673 if blo < bhi:
2025-07-01 03:04:13.678 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:13.682 else:
2025-07-01 03:04:13.687 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:13.691 elif blo < bhi:
2025-07-01 03:04:13.695 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:13.700
2025-07-01 03:04:13.704 >       yield from g
2025-07-01 03:04:13.708
2025-07-01 03:04:13.713 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:13.717 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:13.722
2025-07-01 03:04:13.726 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:13.731 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:13.737 alo = 366, ahi = 1101
2025-07-01 03:04:13.742 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:13.747 blo = 366, bhi = 1101
2025-07-01 03:04:13.751
2025-07-01 03:04:13.756 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:13.760 r"""
2025-07-01 03:04:13.765 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:13.770 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:13.774 synch point, and intraline difference marking is done on the
2025-07-01 03:04:13.779 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:13.783
2025-07-01 03:04:13.788 Example:
2025-07-01 03:04:13.792
2025-07-01 03:04:13.796 >>> d = Differ()
2025-07-01 03:04:13.801 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:13.806 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:13.810 >>> print(''.join(results), end="")
2025-07-01 03:04:13.815 - abcDefghiJkl
2025-07-01 03:04:13.823 + abcdefGhijkl
2025-07-01 03:04:13.832 """
2025-07-01 03:04:13.837
2025-07-01 03:04:13.841 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:13.846 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:13.851 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:13.856 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:13.860 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:13.864
2025-07-01 03:04:13.870 # search for the pair that matches best without being identical
2025-07-01 03:04:13.874 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:13.879 # on junk -- unless we have to)
2025-07-01 03:04:13.883 for j in range(blo, bhi):
2025-07-01 03:04:13.887 bj = b[j]
2025-07-01 03:04:13.892 cruncher.set_seq2(bj)
2025-07-01 03:04:13.897 for i in range(alo, ahi):
2025-07-01 03:04:13.903 ai = a[i]
2025-07-01 03:04:13.908 if ai == bj:
2025-07-01 03:04:13.912 if eqi is None:
2025-07-01 03:04:13.917 eqi, eqj = i, j
2025-07-01 03:04:13.921 continue
2025-07-01 03:04:13.925 cruncher.set_seq1(ai)
2025-07-01 03:04:13.930 # computing similarity is expensive, so use the quick
2025-07-01 03:04:13.935 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:13.940 # compares by a factor of 3.
2025-07-01 03:04:13.945 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:13.951 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:13.956 # of the computation is cached by cruncher
2025-07-01 03:04:13.962 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:13.968 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:13.973 cruncher.ratio() > best_ratio:
2025-07-01 03:04:13.979 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:13.983 if best_ratio < cutoff:
2025-07-01 03:04:13.988 # no non-identical "pretty close" pair
2025-07-01 03:04:13.992 if eqi is None:
2025-07-01 03:04:13.996 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:14.001 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:14.005 return
2025-07-01 03:04:14.009 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:14.014 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:14.018 else:
2025-07-01 03:04:14.022 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:14.026 eqi = None
2025-07-01 03:04:14.031
2025-07-01 03:04:14.035 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:14.039 # identical
2025-07-01 03:04:14.043
2025-07-01 03:04:14.048 # pump out diffs from before the synch point
2025-07-01 03:04:14.053 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:14.057
2025-07-01 03:04:14.061 # do intraline marking on the synch pair
2025-07-01 03:04:14.066 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:14.070 if eqi is None:
2025-07-01 03:04:14.075 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:14.079 atags = btags = ""
2025-07-01 03:04:14.084 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:14.089 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:14.093 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:14.098 if tag == 'replace':
2025-07-01 03:04:14.103 atags += '^' * la
2025-07-01 03:04:14.107 btags += '^' * lb
2025-07-01 03:04:14.111 elif tag == 'delete':
2025-07-01 03:04:14.116 atags += '-' * la
2025-07-01 03:04:14.120 elif tag == 'insert':
2025-07-01 03:04:14.124 btags += '+' * lb
2025-07-01 03:04:14.129 elif tag == 'equal':
2025-07-01 03:04:14.133 atags += ' ' * la
2025-07-01 03:04:14.138 btags += ' ' * lb
2025-07-01 03:04:14.142 else:
2025-07-01 03:04:14.146 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:14.151 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:14.155 else:
2025-07-01 03:04:14.159 # the synch pair is identical
2025-07-01 03:04:14.164 yield '  ' + aelt
2025-07-01 03:04:14.168
2025-07-01 03:04:14.173 # pump out diffs from after the synch point
2025-07-01 03:04:14.178 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:14.182
2025-07-01 03:04:14.187 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:14.191 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:14.196
2025-07-01 03:04:14.201 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:14.206 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:14.211 alo = 367, ahi = 1101
2025-07-01 03:04:14.217 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:14.221 blo = 367, bhi = 1101
2025-07-01 03:04:14.226
2025-07-01 03:04:14.231 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:14.235 g = []
2025-07-01 03:04:14.239 if alo < ahi:
2025-07-01 03:04:14.244 if blo < bhi:
2025-07-01 03:04:14.249 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:14.253 else:
2025-07-01 03:04:14.257 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:14.262 elif blo < bhi:
2025-07-01 03:04:14.266 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:14.271
2025-07-01 03:04:14.275 >       yield from g
2025-07-01 03:04:14.280
2025-07-01 03:04:14.284 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:14.289 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:14.293
2025-07-01 03:04:14.298 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:14.303 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:14.307 alo = 367, ahi = 1101
2025-07-01 03:04:14.312 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:14.316 blo = 367, bhi = 1101
2025-07-01 03:04:14.321
2025-07-01 03:04:14.325 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:14.329 r"""
2025-07-01 03:04:14.334 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:14.339 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:14.343 synch point, and intraline difference marking is done on the
2025-07-01 03:04:14.348 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:14.352
2025-07-01 03:04:14.356 Example:
2025-07-01 03:04:14.361
2025-07-01 03:04:14.365 >>> d = Differ()
2025-07-01 03:04:14.369 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:14.374 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:14.378 >>> print(''.join(results), end="")
2025-07-01 03:04:14.383 - abcDefghiJkl
2025-07-01 03:04:14.393 + abcdefGhijkl
2025-07-01 03:04:14.402 """
2025-07-01 03:04:14.410
2025-07-01 03:04:14.416 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:14.421 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:14.426 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:14.430 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:14.435 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:14.440
2025-07-01 03:04:14.444 # search for the pair that matches best without being identical
2025-07-01 03:04:14.449 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:14.453 # on junk -- unless we have to)
2025-07-01 03:04:14.458 for j in range(blo, bhi):
2025-07-01 03:04:14.462 bj = b[j]
2025-07-01 03:04:14.467 cruncher.set_seq2(bj)
2025-07-01 03:04:14.473 for i in range(alo, ahi):
2025-07-01 03:04:14.477 ai = a[i]
2025-07-01 03:04:14.481 if ai == bj:
2025-07-01 03:04:14.486 if eqi is None:
2025-07-01 03:04:14.490 eqi, eqj = i, j
2025-07-01 03:04:14.494 continue
2025-07-01 03:04:14.499 cruncher.set_seq1(ai)
2025-07-01 03:04:14.503 # computing similarity is expensive, so use the quick
2025-07-01 03:04:14.508 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:14.512 # compares by a factor of 3.
2025-07-01 03:04:14.516 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:14.521 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:14.525 # of the computation is cached by cruncher
2025-07-01 03:04:14.529 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:14.534 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:14.538 cruncher.ratio() > best_ratio:
2025-07-01 03:04:14.543 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:14.547 if best_ratio < cutoff:
2025-07-01 03:04:14.551 # no non-identical "pretty close" pair
2025-07-01 03:04:14.556 if eqi is None:
2025-07-01 03:04:14.560 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:14.564 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:14.569 return
2025-07-01 03:04:14.573 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:14.578 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:14.582 else:
2025-07-01 03:04:14.586 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:14.591 eqi = None
2025-07-01 03:04:14.595
2025-07-01 03:04:14.599 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:14.604 # identical
2025-07-01 03:04:14.608
2025-07-01 03:04:14.613 # pump out diffs from before the synch point
2025-07-01 03:04:14.618 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:14.622
2025-07-01 03:04:14.627 # do intraline marking on the synch pair
2025-07-01 03:04:14.631 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:14.635 if eqi is None:
2025-07-01 03:04:14.640 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:14.644 atags = btags = ""
2025-07-01 03:04:14.648 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:14.653 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:14.657 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:14.661 if tag == 'replace':
2025-07-01 03:04:14.666 atags += '^' * la
2025-07-01 03:04:14.670 btags += '^' * lb
2025-07-01 03:04:14.674 elif tag == 'delete':
2025-07-01 03:04:14.680 atags += '-' * la
2025-07-01 03:04:14.684 elif tag == 'insert':
2025-07-01 03:04:14.688 btags += '+' * lb
2025-07-01 03:04:14.693 elif tag == 'equal':
2025-07-01 03:04:14.697 atags += ' ' * la
2025-07-01 03:04:14.701 btags += ' ' * lb
2025-07-01 03:04:14.706 else:
2025-07-01 03:04:14.710 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:14.715 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:14.719 else:
2025-07-01 03:04:14.723 # the synch pair is identical
2025-07-01 03:04:14.728 yield '  ' + aelt
2025-07-01 03:04:14.732
2025-07-01 03:04:14.736 # pump out diffs from after the synch point
2025-07-01 03:04:14.741 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:14.745
2025-07-01 03:04:14.750 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:14.754 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:14.759
2025-07-01 03:04:14.764 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:14.769 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:14.773 alo = 368, ahi = 1101
2025-07-01 03:04:14.779 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:14.783 blo = 368, bhi = 1101
2025-07-01 03:04:14.788
2025-07-01 03:04:14.792 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:14.796 g = []
2025-07-01 03:04:14.801 if alo < ahi:
2025-07-01 03:04:14.805 if blo < bhi:
2025-07-01 03:04:14.810 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:14.814 else:
2025-07-01 03:04:14.818 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:14.823 elif blo < bhi:
2025-07-01 03:04:14.827 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:14.831
2025-07-01 03:04:14.836 >       yield from g
2025-07-01 03:04:14.840
2025-07-01 03:04:14.845 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:14.850 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:14.854
2025-07-01 03:04:14.859 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:14.864 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:14.868 alo = 368, ahi = 1101
2025-07-01 03:04:14.873 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:14.878 blo = 368, bhi = 1101
2025-07-01 03:04:14.882
2025-07-01 03:04:14.887 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:14.891 r"""
2025-07-01 03:04:14.895 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:14.900 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:14.904 synch point, and intraline difference marking is done on the
2025-07-01 03:04:14.909 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:14.914
2025-07-01 03:04:14.918 Example:
2025-07-01 03:04:14.922
2025-07-01 03:04:14.927 >>> d = Differ()
2025-07-01 03:04:14.931 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:14.936 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:14.941 >>> print(''.join(results), end="")
2025-07-01 03:04:14.945 - abcDefghiJkl
2025-07-01 03:04:14.954 + abcdefGhijkl
2025-07-01 03:04:14.963 """
2025-07-01 03:04:14.967
2025-07-01 03:04:14.972 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:14.976 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:14.981 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:14.986 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:14.990 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:14.994
2025-07-01 03:04:14.999 # search for the pair that matches best without being identical
2025-07-01 03:04:15.003 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:15.008 # on junk -- unless we have to)
2025-07-01 03:04:15.012 for j in range(blo, bhi):
2025-07-01 03:04:15.017 bj = b[j]
2025-07-01 03:04:15.021 cruncher.set_seq2(bj)
2025-07-01 03:04:15.026 for i in range(alo, ahi):
2025-07-01 03:04:15.031 ai = a[i]
2025-07-01 03:04:15.036 if ai == bj:
2025-07-01 03:04:15.040 if eqi is None:
2025-07-01 03:04:15.045 eqi, eqj = i, j
2025-07-01 03:04:15.053 continue
2025-07-01 03:04:15.071 cruncher.set_seq1(ai)
2025-07-01 03:04:15.086 # computing similarity is expensive, so use the quick
2025-07-01 03:04:15.098 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:15.110 # compares by a factor of 3.
2025-07-01 03:04:15.122 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:15.134 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:15.146 # of the computation is cached by cruncher
2025-07-01 03:04:15.158 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:15.170 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:15.182 cruncher.ratio() > best_ratio:
2025-07-01 03:04:15.194 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:15.206 if best_ratio < cutoff:
2025-07-01 03:04:15.214 # no non-identical "pretty close" pair
2025-07-01 03:04:15.230 if eqi is None:
2025-07-01 03:04:15.236 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:15.247 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:15.259 return
2025-07-01 03:04:15.272 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:15.283 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:15.291 else:
2025-07-01 03:04:15.298 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:15.310 eqi = None
2025-07-01 03:04:15.325
2025-07-01 03:04:15.338 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:15.348 # identical
2025-07-01 03:04:15.356
2025-07-01 03:04:15.370 # pump out diffs from before the synch point
2025-07-01 03:04:15.385 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:15.398
2025-07-01 03:04:15.406 # do intraline marking on the synch pair
2025-07-01 03:04:15.422 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:15.435 if eqi is None:
2025-07-01 03:04:15.450 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:15.458 atags = btags = ""
2025-07-01 03:04:15.470 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:15.478 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:15.490 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:15.498 if tag == 'replace':
2025-07-01 03:04:15.510 atags += '^' * la
2025-07-01 03:04:15.517 btags += '^' * lb
2025-07-01 03:04:15.534 elif tag == 'delete':
2025-07-01 03:04:15.542 atags += '-' * la
2025-07-01 03:04:15.554 elif tag == 'insert':
2025-07-01 03:04:15.562 btags += '+' * lb
2025-07-01 03:04:15.574 elif tag == 'equal':
2025-07-01 03:04:15.582 atags += ' ' * la
2025-07-01 03:04:15.591 btags += ' ' * lb
2025-07-01 03:04:15.601 else:
2025-07-01 03:04:15.611 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:15.626 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:15.633 else:
2025-07-01 03:04:15.646 # the synch pair is identical
2025-07-01 03:04:15.658 yield '  ' + aelt
2025-07-01 03:04:15.669
2025-07-01 03:04:15.682 # pump out diffs from after the synch point
2025-07-01 03:04:15.695 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:15.706
2025-07-01 03:04:15.722 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:15.730 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:15.742
2025-07-01 03:04:15.749 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:15.762 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:15.778 alo = 369, ahi = 1101
2025-07-01 03:04:15.786 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:15.798 blo = 369, bhi = 1101
2025-07-01 03:04:15.810
2025-07-01 03:04:15.826 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:15.834 g = []
2025-07-01 03:04:15.850 if alo < ahi:
2025-07-01 03:04:15.862 if blo < bhi:
2025-07-01 03:04:15.870 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:15.882 else:
2025-07-01 03:04:15.892 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:15.904 elif blo < bhi:
2025-07-01 03:04:15.914 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:15.926
2025-07-01 03:04:15.933 >       yield from g
2025-07-01 03:04:15.946
2025-07-01 03:04:15.960 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:15.974 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:15.986
2025-07-01 03:04:15.997 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:16.008 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:16.026 alo = 369, ahi = 1101
2025-07-01 03:04:16.033 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:16.046 blo = 369, bhi = 1101
2025-07-01 03:04:16.058
2025-07-01 03:04:16.066 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:16.082 r"""
2025-07-01 03:04:16.090 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:16.106 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:16.115 synch point, and intraline difference marking is done on the
2025-07-01 03:04:16.128 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:16.138
2025-07-01 03:04:16.154 Example:
2025-07-01 03:04:16.166
2025-07-01 03:04:16.174 >>> d = Differ()
2025-07-01 03:04:16.188 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:16.198 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:16.205 >>> print(''.join(results), end="")
2025-07-01 03:04:16.222 - abcDefghiJkl
2025-07-01 03:04:16.246 + abcdefGhijkl
2025-07-01 03:04:16.266 """
2025-07-01 03:04:16.274
2025-07-01 03:04:16.288 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:16.296 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:16.301 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:16.305 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:16.310 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:16.314
2025-07-01 03:04:16.319 # search for the pair that matches best without being identical
2025-07-01 03:04:16.323 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:16.328 # on junk -- unless we have to)
2025-07-01 03:04:16.332 for j in range(blo, bhi):
2025-07-01 03:04:16.337 bj = b[j]
2025-07-01 03:04:16.342 cruncher.set_seq2(bj)
2025-07-01 03:04:16.346 for i in range(alo, ahi):
2025-07-01 03:04:16.351 ai = a[i]
2025-07-01 03:04:16.356 if ai == bj:
2025-07-01 03:04:16.361 if eqi is None:
2025-07-01 03:04:16.366 eqi, eqj = i, j
2025-07-01 03:04:16.371 continue
2025-07-01 03:04:16.375 cruncher.set_seq1(ai)
2025-07-01 03:04:16.380 # computing similarity is expensive, so use the quick
2025-07-01 03:04:16.385 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:16.390 # compares by a factor of 3.
2025-07-01 03:04:16.395 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:16.400 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:16.405 # of the computation is cached by cruncher
2025-07-01 03:04:16.409 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:16.414 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:16.419 cruncher.ratio() > best_ratio:
2025-07-01 03:04:16.423 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:16.427 if best_ratio < cutoff:
2025-07-01 03:04:16.432 # no non-identical "pretty close" pair
2025-07-01 03:04:16.436 if eqi is None:
2025-07-01 03:04:16.441 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:16.448 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:16.454 return
2025-07-01 03:04:16.458 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:16.465 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:16.469 else:
2025-07-01 03:04:16.474 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:16.480 eqi = None
2025-07-01 03:04:16.484
2025-07-01 03:04:16.489 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:16.493 # identical
2025-07-01 03:04:16.497
2025-07-01 03:04:16.506 # pump out diffs from before the synch point
2025-07-01 03:04:16.514 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:16.522
2025-07-01 03:04:16.530 # do intraline marking on the synch pair
2025-07-01 03:04:16.538 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:16.549 if eqi is None:
2025-07-01 03:04:16.557 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:16.570 atags = btags = ""
2025-07-01 03:04:16.577 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:16.586 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:16.601 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:16.615 if tag == 'replace':
2025-07-01 03:04:16.634 atags += '^' * la
2025-07-01 03:04:16.646 btags += '^' * lb
2025-07-01 03:04:16.660 elif tag == 'delete':
2025-07-01 03:04:16.678 atags += '-' * la
2025-07-01 03:04:16.690 elif tag == 'insert':
2025-07-01 03:04:16.702 btags += '+' * lb
2025-07-01 03:04:16.711 elif tag == 'equal':
2025-07-01 03:04:16.726 atags += ' ' * la
2025-07-01 03:04:16.734 btags += ' ' * lb
2025-07-01 03:04:16.742 else:
2025-07-01 03:04:16.754 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:16.762 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:16.774 else:
2025-07-01 03:04:16.782 # the synch pair is identical
2025-07-01 03:04:16.794 yield '  ' + aelt
2025-07-01 03:04:16.806
2025-07-01 03:04:16.814 # pump out diffs from after the synch point
2025-07-01 03:04:16.830 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:16.842
2025-07-01 03:04:16.850 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:16.858 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:16.870
2025-07-01 03:04:16.878 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:16.890 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:16.902 alo = 370, ahi = 1101
2025-07-01 03:04:16.914 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:16.926 blo = 370, bhi = 1101
2025-07-01 03:04:16.934
2025-07-01 03:04:16.946 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:16.958 g = []
2025-07-01 03:04:16.966 if alo < ahi:
2025-07-01 03:04:16.978 if blo < bhi:
2025-07-01 03:04:16.986 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:16.998 else:
2025-07-01 03:04:17.010 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:17.026 elif blo < bhi:
2025-07-01 03:04:17.034 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:17.042
2025-07-01 03:04:17.054 >       yield from g
2025-07-01 03:04:17.062
2025-07-01 03:04:17.074 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:17.082 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:17.094
2025-07-01 03:04:17.105 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:17.122 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:17.134 alo = 370, ahi = 1101
2025-07-01 03:04:17.148 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:17.158 blo = 370, bhi = 1101
2025-07-01 03:04:17.163
2025-07-01 03:04:17.181 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:17.197 r"""
2025-07-01 03:04:17.210 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:17.219 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:17.234 synch point, and intraline difference marking is done on the
2025-07-01 03:04:17.243 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:17.258
2025-07-01 03:04:17.266 Example:
2025-07-01 03:04:17.274
2025-07-01 03:04:17.286 >>> d = Differ()
2025-07-01 03:04:17.298 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:17.306 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:17.318 >>> print(''.join(results), end="")
2025-07-01 03:04:17.326 - abcDefghiJkl
2025-07-01 03:04:17.346 + abcdefGhijkl
2025-07-01 03:04:17.366 """
2025-07-01 03:04:17.378
2025-07-01 03:04:17.386 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:17.399 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:17.410 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:17.417 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:17.430 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:17.442
2025-07-01 03:04:17.456 # search for the pair that matches best without being identical
2025-07-01 03:04:17.470 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:17.486 # on junk -- unless we have to)
2025-07-01 03:04:17.494 for j in range(blo, bhi):
2025-07-01 03:04:17.502 bj = b[j]
2025-07-01 03:04:17.512 cruncher.set_seq2(bj)
2025-07-01 03:04:17.524 for i in range(alo, ahi):
2025-07-01 03:04:17.539 ai = a[i]
2025-07-01 03:04:17.553 if ai == bj:
2025-07-01 03:04:17.562 if eqi is None:
2025-07-01 03:04:17.572 eqi, eqj = i, j
2025-07-01 03:04:17.586 continue
2025-07-01 03:04:17.594 cruncher.set_seq1(ai)
2025-07-01 03:04:17.602 # computing similarity is expensive, so use the quick
2025-07-01 03:04:17.618 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:17.630 # compares by a factor of 3.
2025-07-01 03:04:17.639 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:17.654 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:17.662 # of the computation is cached by cruncher
2025-07-01 03:04:17.673 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:17.686 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:17.698 cruncher.ratio() > best_ratio:
2025-07-01 03:04:17.710 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:17.719 if best_ratio < cutoff:
2025-07-01 03:04:17.729 # no non-identical "pretty close" pair
2025-07-01 03:04:17.737 if eqi is None:
2025-07-01 03:04:17.750 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:17.767 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:17.782 return
2025-07-01 03:04:17.801 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:17.808 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:17.826 else:
2025-07-01 03:04:17.834 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:17.846 eqi = None
2025-07-01 03:04:17.858
2025-07-01 03:04:17.871 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:17.878 # identical
2025-07-01 03:04:17.890
2025-07-01 03:04:17.898 # pump out diffs from before the synch point
2025-07-01 03:04:17.910 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:17.918
2025-07-01 03:04:17.930 # do intraline marking on the synch pair
2025-07-01 03:04:17.945 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:17.959 if eqi is None:
2025-07-01 03:04:17.974 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:17.986 atags = btags = ""
2025-07-01 03:04:17.998 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:18.014 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:18.022 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:18.030 if tag == 'replace':
2025-07-01 03:04:18.038 atags += '^' * la
2025-07-01 03:04:18.054 btags += '^' * lb
2025-07-01 03:04:18.063 elif tag == 'delete':
2025-07-01 03:04:18.076 atags += '-' * la
2025-07-01 03:04:18.083 elif tag == 'insert':
2025-07-01 03:04:18.089 btags += '+' * lb
2025-07-01 03:04:18.095 elif tag == 'equal':
2025-07-01 03:04:18.100 atags += ' ' * la
2025-07-01 03:04:18.105 btags += ' ' * lb
2025-07-01 03:04:18.111 else:
2025-07-01 03:04:18.117 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:18.124 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:18.130 else:
2025-07-01 03:04:18.136 # the synch pair is identical
2025-07-01 03:04:18.142 yield '  ' + aelt
2025-07-01 03:04:18.148
2025-07-01 03:04:18.154 # pump out diffs from after the synch point
2025-07-01 03:04:18.159 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:18.165
2025-07-01 03:04:18.171 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:18.177 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:18.182
2025-07-01 03:04:18.188 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:18.195 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:18.201 alo = 371, ahi = 1101
2025-07-01 03:04:18.210 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:18.221 blo = 371, bhi = 1101
2025-07-01 03:04:18.229
2025-07-01 03:04:18.234 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:18.239 g = []
2025-07-01 03:04:18.243 if alo < ahi:
2025-07-01 03:04:18.247 if blo < bhi:
2025-07-01 03:04:18.252 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:18.266 else:
2025-07-01 03:04:18.282 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:18.290 elif blo < bhi:
2025-07-01 03:04:18.302 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:18.314
2025-07-01 03:04:18.326 >       yield from g
2025-07-01 03:04:18.338
2025-07-01 03:04:18.346 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:18.358 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:18.366
2025-07-01 03:04:18.378 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:18.394 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:18.402 alo = 371, ahi = 1101
2025-07-01 03:04:18.418 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:18.426 blo = 371, bhi = 1101
2025-07-01 03:04:18.434
2025-07-01 03:04:18.446 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:18.454 r"""
2025-07-01 03:04:18.466 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:18.474 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:18.486 synch point, and intraline difference marking is done on the
2025-07-01 03:04:18.494 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:18.506
2025-07-01 03:04:18.514 Example:
2025-07-01 03:04:18.530
2025-07-01 03:04:18.538 >>> d = Differ()
2025-07-01 03:04:18.554 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:18.567 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:18.578 >>> print(''.join(results), end="")
2025-07-01 03:04:18.593 - abcDefghiJkl
2025-07-01 03:04:18.617 + abcdefGhijkl
2025-07-01 03:04:18.637 """
2025-07-01 03:04:18.649
2025-07-01 03:04:18.660 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:18.673 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:18.686 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:18.694 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:18.705 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:18.722
2025-07-01 03:04:18.732 # search for the pair that matches best without being identical
2025-07-01 03:04:18.745 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:18.754 # on junk -- unless we have to)
2025-07-01 03:04:18.762 for j in range(blo, bhi):
2025-07-01 03:04:18.777 bj = b[j]
2025-07-01 03:04:18.786 cruncher.set_seq2(bj)
2025-07-01 03:04:18.796 for i in range(alo, ahi):
2025-07-01 03:04:18.809 ai = a[i]
2025-07-01 03:04:18.821 if ai == bj:
2025-07-01 03:04:18.837 if eqi is None:
2025-07-01 03:04:18.849 eqi, eqj = i, j
2025-07-01 03:04:18.854 continue
2025-07-01 03:04:18.868 cruncher.set_seq1(ai)
2025-07-01 03:04:18.882 # computing similarity is expensive, so use the quick
2025-07-01 03:04:18.890 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:18.902 # compares by a factor of 3.
2025-07-01 03:04:18.910 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:18.922 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:18.929 # of the computation is cached by cruncher
2025-07-01 03:04:18.941 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:18.950 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:18.962 cruncher.ratio() > best_ratio:
2025-07-01 03:04:18.970 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:18.982 if best_ratio < cutoff:
2025-07-01 03:04:18.990 # no non-identical "pretty close" pair
2025-07-01 03:04:19.002 if eqi is None:
2025-07-01 03:04:19.010 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:19.022 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:19.030 return
2025-07-01 03:04:19.042 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:19.050 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:19.066 else:
2025-07-01 03:04:19.073 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:19.090 eqi = None
2025-07-01 03:04:19.098
2025-07-01 03:04:19.110 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:19.117 # identical
2025-07-01 03:04:19.126
2025-07-01 03:04:19.142 # pump out diffs from before the synch point
2025-07-01 03:04:19.150 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:19.166
2025-07-01 03:04:19.174 # do intraline marking on the synch pair
2025-07-01 03:04:19.186 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:19.194 if eqi is None:
2025-07-01 03:04:19.206 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:19.214 atags = btags = ""
2025-07-01 03:04:19.226 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:19.238 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:19.246 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:19.258 if tag == 'replace':
2025-07-01 03:04:19.274 atags += '^' * la
2025-07-01 03:04:19.282 btags += '^' * lb
2025-07-01 03:04:19.298 elif tag == 'delete':
2025-07-01 03:04:19.306 atags += '-' * la
2025-07-01 03:04:19.322 elif tag == 'insert':
2025-07-01 03:04:19.330 btags += '+' * lb
2025-07-01 03:04:19.342 elif tag == 'equal':
2025-07-01 03:04:19.354 atags += ' ' * la
2025-07-01 03:04:19.362 btags += ' ' * lb
2025-07-01 03:04:19.374 else:
2025-07-01 03:04:19.386 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:19.394 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:19.406 else:
2025-07-01 03:04:19.414 # the synch pair is identical
2025-07-01 03:04:19.425 yield '  ' + aelt
2025-07-01 03:04:19.444
2025-07-01 03:04:19.451 # pump out diffs from after the synch point
2025-07-01 03:04:19.459 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:19.474
2025-07-01 03:04:19.486 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:19.495 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:19.506
2025-07-01 03:04:19.521 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:19.530 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:19.540 alo = 372, ahi = 1101
2025-07-01 03:04:19.550 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:19.561 blo = 372, bhi = 1101
2025-07-01 03:04:19.565
2025-07-01 03:04:19.582 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:19.596 g = []
2025-07-01 03:04:19.608 if alo < ahi:
2025-07-01 03:04:19.620 if blo < bhi:
2025-07-01 03:04:19.630 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:19.646 else:
2025-07-01 03:04:19.654 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:19.666 elif blo < bhi:
2025-07-01 03:04:19.673 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:19.690
2025-07-01 03:04:19.702 >       yield from g
2025-07-01 03:04:19.710
2025-07-01 03:04:19.724 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:19.734 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:19.750
2025-07-01 03:04:19.762 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:19.772 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:19.781 alo = 372, ahi = 1101
2025-07-01 03:04:19.795 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:19.806 blo = 372, bhi = 1101
2025-07-01 03:04:19.817
2025-07-01 03:04:19.825 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:19.845 r"""
2025-07-01 03:04:19.860 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:19.870 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:19.877 synch point, and intraline difference marking is done on the
2025-07-01 03:04:19.894 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:19.903
2025-07-01 03:04:19.910 Example:
2025-07-01 03:04:19.926
2025-07-01 03:04:19.938 >>> d = Differ()
2025-07-01 03:04:19.947 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:19.958 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:19.970 >>> print(''.join(results), end="")
2025-07-01 03:04:19.982 - abcDefghiJkl
2025-07-01 03:04:20.002 + abcdefGhijkl
2025-07-01 03:04:20.020 """
2025-07-01 03:04:20.026
2025-07-01 03:04:20.031 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:20.037 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:20.042 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:20.047 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:20.052 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:20.058
2025-07-01 03:04:20.063 # search for the pair that matches best without being identical
2025-07-01 03:04:20.068 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:20.072 # on junk -- unless we have to)
2025-07-01 03:04:20.076 for j in range(blo, bhi):
2025-07-01 03:04:20.081 bj = b[j]
2025-07-01 03:04:20.085 cruncher.set_seq2(bj)
2025-07-01 03:04:20.090 for i in range(alo, ahi):
2025-07-01 03:04:20.095 ai = a[i]
2025-07-01 03:04:20.100 if ai == bj:
2025-07-01 03:04:20.104 if eqi is None:
2025-07-01 03:04:20.109 eqi, eqj = i, j
2025-07-01 03:04:20.113 continue
2025-07-01 03:04:20.118 cruncher.set_seq1(ai)
2025-07-01 03:04:20.122 # computing similarity is expensive, so use the quick
2025-07-01 03:04:20.127 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:20.131 # compares by a factor of 3.
2025-07-01 03:04:20.136 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:20.141 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:20.145 # of the computation is cached by cruncher
2025-07-01 03:04:20.150 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:20.154 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:20.159 cruncher.ratio() > best_ratio:
2025-07-01 03:04:20.163 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:20.167 if best_ratio < cutoff:
2025-07-01 03:04:20.172 # no non-identical "pretty close" pair
2025-07-01 03:04:20.176 if eqi is None:
2025-07-01 03:04:20.181 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:20.185 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:20.189 return
2025-07-01 03:04:20.194 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:20.198 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:20.203 else:
2025-07-01 03:04:20.207 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:20.212 eqi = None
2025-07-01 03:04:20.216
2025-07-01 03:04:20.220 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:20.225 # identical
2025-07-01 03:04:20.229
2025-07-01 03:04:20.233 # pump out diffs from before the synch point
2025-07-01 03:04:20.238 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:20.242
2025-07-01 03:04:20.247 # do intraline marking on the synch pair
2025-07-01 03:04:20.252 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:20.256 if eqi is None:
2025-07-01 03:04:20.262 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:20.266 atags = btags = ""
2025-07-01 03:04:20.270 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:20.275 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:20.281 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:20.286 if tag == 'replace':
2025-07-01 03:04:20.290 atags += '^' * la
2025-07-01 03:04:20.294 btags += '^' * lb
2025-07-01 03:04:20.299 elif tag == 'delete':
2025-07-01 03:04:20.303 atags += '-' * la
2025-07-01 03:04:20.308 elif tag == 'insert':
2025-07-01 03:04:20.312 btags += '+' * lb
2025-07-01 03:04:20.316 elif tag == 'equal':
2025-07-01 03:04:20.321 atags += ' ' * la
2025-07-01 03:04:20.325 btags += ' ' * lb
2025-07-01 03:04:20.329 else:
2025-07-01 03:04:20.335 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:20.339 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:20.344 else:
2025-07-01 03:04:20.348 # the synch pair is identical
2025-07-01 03:04:20.352 yield '  ' + aelt
2025-07-01 03:04:20.356
2025-07-01 03:04:20.361 # pump out diffs from after the synch point
2025-07-01 03:04:20.366 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:20.370
2025-07-01 03:04:20.375 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:20.379 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:20.384
2025-07-01 03:04:20.388 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:20.393 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:20.398 alo = 373, ahi = 1101
2025-07-01 03:04:20.403 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:20.407 blo = 373, bhi = 1101
2025-07-01 03:04:20.411
2025-07-01 03:04:20.416 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:20.420 g = []
2025-07-01 03:04:20.425 if alo < ahi:
2025-07-01 03:04:20.429 if blo < bhi:
2025-07-01 03:04:20.434 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:20.438 else:
2025-07-01 03:04:20.442 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:20.447 elif blo < bhi:
2025-07-01 03:04:20.451 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:20.455
2025-07-01 03:04:20.460 >       yield from g
2025-07-01 03:04:20.464
2025-07-01 03:04:20.469 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:20.473 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:20.478
2025-07-01 03:04:20.482 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:20.487 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:20.493 alo = 373, ahi = 1101
2025-07-01 03:04:20.498 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:20.502 blo = 373, bhi = 1101
2025-07-01 03:04:20.507
2025-07-01 03:04:20.511 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:20.515 r"""
2025-07-01 03:04:20.520 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:20.525 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:20.529 synch point, and intraline difference marking is done on the
2025-07-01 03:04:20.534 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:20.538
2025-07-01 03:04:20.543 Example:
2025-07-01 03:04:20.547
2025-07-01 03:04:20.552 >>> d = Differ()
2025-07-01 03:04:20.556 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:20.561 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:20.566 >>> print(''.join(results), end="")
2025-07-01 03:04:20.570 - abcDefghiJkl
2025-07-01 03:04:20.579 + abcdefGhijkl
2025-07-01 03:04:20.587 """
2025-07-01 03:04:20.594
2025-07-01 03:04:20.598 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:20.603 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:20.609 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:20.614 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:20.618 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:20.622
2025-07-01 03:04:20.627 # search for the pair that matches best without being identical
2025-07-01 03:04:20.631 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:20.636 # on junk -- unless we have to)
2025-07-01 03:04:20.645 for j in range(blo, bhi):
2025-07-01 03:04:20.649 bj = b[j]
2025-07-01 03:04:20.654 cruncher.set_seq2(bj)
2025-07-01 03:04:20.658 for i in range(alo, ahi):
2025-07-01 03:04:20.663 ai = a[i]
2025-07-01 03:04:20.667 if ai == bj:
2025-07-01 03:04:20.672 if eqi is None:
2025-07-01 03:04:20.678 eqi, eqj = i, j
2025-07-01 03:04:20.685 continue
2025-07-01 03:04:20.689 cruncher.set_seq1(ai)
2025-07-01 03:04:20.694 # computing similarity is expensive, so use the quick
2025-07-01 03:04:20.699 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:20.703 # compares by a factor of 3.
2025-07-01 03:04:20.708 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:20.712 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:20.717 # of the computation is cached by cruncher
2025-07-01 03:04:20.721 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:20.726 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:20.730 cruncher.ratio() > best_ratio:
2025-07-01 03:04:20.735 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:20.739 if best_ratio < cutoff:
2025-07-01 03:04:20.744 # no non-identical "pretty close" pair
2025-07-01 03:04:20.748 if eqi is None:
2025-07-01 03:04:20.753 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:20.757 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:20.762 return
2025-07-01 03:04:20.767 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:20.771 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:20.776 else:
2025-07-01 03:04:20.780 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:20.784 eqi = None
2025-07-01 03:04:20.789
2025-07-01 03:04:20.793 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:20.797 # identical
2025-07-01 03:04:20.802
2025-07-01 03:04:20.806 # pump out diffs from before the synch point
2025-07-01 03:04:20.811 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:20.816
2025-07-01 03:04:20.820 # do intraline marking on the synch pair
2025-07-01 03:04:20.825 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:20.829 if eqi is None:
2025-07-01 03:04:20.833 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:20.838 atags = btags = ""
2025-07-01 03:04:20.842 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:20.846 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:20.851 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:20.855 if tag == 'replace':
2025-07-01 03:04:20.860 atags += '^' * la
2025-07-01 03:04:20.864 btags += '^' * lb
2025-07-01 03:04:20.868 elif tag == 'delete':
2025-07-01 03:04:20.873 atags += '-' * la
2025-07-01 03:04:20.877 elif tag == 'insert':
2025-07-01 03:04:20.881 btags += '+' * lb
2025-07-01 03:04:20.886 elif tag == 'equal':
2025-07-01 03:04:20.890 atags += ' ' * la
2025-07-01 03:04:20.894 btags += ' ' * lb
2025-07-01 03:04:20.899 else:
2025-07-01 03:04:20.905 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:20.911 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:20.917 else:
2025-07-01 03:04:20.924 # the synch pair is identical
2025-07-01 03:04:20.930 yield '  ' + aelt
2025-07-01 03:04:20.936
2025-07-01 03:04:20.942 # pump out diffs from after the synch point
2025-07-01 03:04:20.947 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:20.953
2025-07-01 03:04:20.961 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:20.982 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:20.990
2025-07-01 03:04:21.002 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:21.013 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:21.025 alo = 374, ahi = 1101
2025-07-01 03:04:21.038 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:21.057 blo = 374, bhi = 1101
2025-07-01 03:04:21.062
2025-07-01 03:04:21.074 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:21.085 g = []
2025-07-01 03:04:21.094 if alo < ahi:
2025-07-01 03:04:21.112 if blo < bhi:
2025-07-01 03:04:21.122 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:21.134 else:
2025-07-01 03:04:21.142 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:21.154 elif blo < bhi:
2025-07-01 03:04:21.162 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:21.170
2025-07-01 03:04:21.182 >       yield from g
2025-07-01 03:04:21.194
2025-07-01 03:04:21.202 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:21.214 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:21.222
2025-07-01 03:04:21.234 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:21.246 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:21.258 alo = 374, ahi = 1101
2025-07-01 03:04:21.270 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:21.278 blo = 374, bhi = 1101
2025-07-01 03:04:21.290
2025-07-01 03:04:21.302 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:21.318 r"""
2025-07-01 03:04:21.326 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:21.342 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:21.350 synch point, and intraline difference marking is done on the
2025-07-01 03:04:21.366 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:21.374
2025-07-01 03:04:21.390 Example:
2025-07-01 03:04:21.398
2025-07-01 03:04:21.410 >>> d = Differ()
2025-07-01 03:04:21.417 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:21.430 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:21.438 >>> print(''.join(results), end="")
2025-07-01 03:04:21.450 - abcDefghiJkl
2025-07-01 03:04:21.469 + abcdefGhijkl
2025-07-01 03:04:21.494 """
2025-07-01 03:04:21.510
2025-07-01 03:04:21.518 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:21.530 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:21.542 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:21.550 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:21.565 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:21.574
2025-07-01 03:04:21.590 # search for the pair that matches best without being identical
2025-07-01 03:04:21.598 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:21.609 # on junk -- unless we have to)
2025-07-01 03:04:21.617 for j in range(blo, bhi):
2025-07-01 03:04:21.629 bj = b[j]
2025-07-01 03:04:21.640 cruncher.set_seq2(bj)
2025-07-01 03:04:21.654 for i in range(alo, ahi):
2025-07-01 03:04:21.661 ai = a[i]
2025-07-01 03:04:21.677 if ai == bj:
2025-07-01 03:04:21.686 if eqi is None:
2025-07-01 03:04:21.698 eqi, eqj = i, j
2025-07-01 03:04:21.706 continue
2025-07-01 03:04:21.717 cruncher.set_seq1(ai)
2025-07-01 03:04:21.725 # computing similarity is expensive, so use the quick
2025-07-01 03:04:21.738 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:21.745 # compares by a factor of 3.
2025-07-01 03:04:21.758 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:21.767 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:21.785 # of the computation is cached by cruncher
2025-07-01 03:04:21.798 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:21.813 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:21.823 cruncher.ratio() > best_ratio:
2025-07-01 03:04:21.833 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:21.849 if best_ratio < cutoff:
2025-07-01 03:04:21.860 # no non-identical "pretty close" pair
2025-07-01 03:04:21.877 if eqi is None:
2025-07-01 03:04:21.890 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:21.897 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:21.910 return
2025-07-01 03:04:21.917 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:21.930 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:21.939 else:
2025-07-01 03:04:21.953 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:21.964 eqi = None
2025-07-01 03:04:21.973
2025-07-01 03:04:21.982 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:21.992 # identical
2025-07-01 03:04:22.002
2025-07-01 03:04:22.014 # pump out diffs from before the synch point
2025-07-01 03:04:22.024 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:22.037
2025-07-01 03:04:22.048 # do intraline marking on the synch pair
2025-07-01 03:04:22.060 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:22.066 if eqi is None:
2025-07-01 03:04:22.077 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:22.086 atags = btags = ""
2025-07-01 03:04:22.098 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:22.110 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:22.122 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:22.130 if tag == 'replace':
2025-07-01 03:04:22.142 atags += '^' * la
2025-07-01 03:04:22.150 btags += '^' * lb
2025-07-01 03:04:22.162 elif tag == 'delete':
2025-07-01 03:04:22.170 atags += '-' * la
2025-07-01 03:04:22.182 elif tag == 'insert':
2025-07-01 03:04:22.194 btags += '+' * lb
2025-07-01 03:04:22.202 elif tag == 'equal':
2025-07-01 03:04:22.214 atags += ' ' * la
2025-07-01 03:04:22.222 btags += ' ' * lb
2025-07-01 03:04:22.234 else:
2025-07-01 03:04:22.242 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:22.254 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:22.262 else:
2025-07-01 03:04:22.278 # the synch pair is identical
2025-07-01 03:04:22.286 yield '  ' + aelt
2025-07-01 03:04:22.298
2025-07-01 03:04:22.306 # pump out diffs from after the synch point
2025-07-01 03:04:22.318 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:22.328
2025-07-01 03:04:22.333 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:22.337 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:22.342
2025-07-01 03:04:22.346 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:22.352 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:22.358 alo = 375, ahi = 1101
2025-07-01 03:04:22.364 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:22.369 blo = 375, bhi = 1101
2025-07-01 03:04:22.374
2025-07-01 03:04:22.379 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:22.384 g = []
2025-07-01 03:04:22.389 if alo < ahi:
2025-07-01 03:04:22.393 if blo < bhi:
2025-07-01 03:04:22.398 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:22.403 else:
2025-07-01 03:04:22.407 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:22.412 elif blo < bhi:
2025-07-01 03:04:22.416 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:22.421
2025-07-01 03:04:22.425 >       yield from g
2025-07-01 03:04:22.430
2025-07-01 03:04:22.435 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:22.439 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:22.444
2025-07-01 03:04:22.448 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:22.453 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:22.457 alo = 375, ahi = 1101
2025-07-01 03:04:22.462 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:22.467 blo = 375, bhi = 1101
2025-07-01 03:04:22.471
2025-07-01 03:04:22.475 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:22.480 r"""
2025-07-01 03:04:22.484 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:22.489 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:22.494 synch point, and intraline difference marking is done on the
2025-07-01 03:04:22.498 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:22.502
2025-07-01 03:04:22.507 Example:
2025-07-01 03:04:22.511
2025-07-01 03:04:22.516 >>> d = Differ()
2025-07-01 03:04:22.520 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:22.524 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:22.529 >>> print(''.join(results), end="")
2025-07-01 03:04:22.533 - abcDefghiJkl
2025-07-01 03:04:22.542 + abcdefGhijkl
2025-07-01 03:04:22.551 """
2025-07-01 03:04:22.556
2025-07-01 03:04:22.561 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:22.566 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:22.571 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:22.575 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:22.580 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:22.584
2025-07-01 03:04:22.589 # search for the pair that matches best without being identical
2025-07-01 03:04:22.594 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:22.599 # on junk -- unless we have to)
2025-07-01 03:04:22.604 for j in range(blo, bhi):
2025-07-01 03:04:22.608 bj = b[j]
2025-07-01 03:04:22.613 cruncher.set_seq2(bj)
2025-07-01 03:04:22.617 for i in range(alo, ahi):
2025-07-01 03:04:22.622 ai = a[i]
2025-07-01 03:04:22.626 if ai == bj:
2025-07-01 03:04:22.631 if eqi is None:
2025-07-01 03:04:22.635 eqi, eqj = i, j
2025-07-01 03:04:22.639 continue
2025-07-01 03:04:22.644 cruncher.set_seq1(ai)
2025-07-01 03:04:22.649 # computing similarity is expensive, so use the quick
2025-07-01 03:04:22.653 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:22.658 # compares by a factor of 3.
2025-07-01 03:04:22.662 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:22.667 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:22.672 # of the computation is cached by cruncher
2025-07-01 03:04:22.677 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:22.681 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:22.686 cruncher.ratio() > best_ratio:
2025-07-01 03:04:22.690 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:22.695 if best_ratio < cutoff:
2025-07-01 03:04:22.699 # no non-identical "pretty close" pair
2025-07-01 03:04:22.704 if eqi is None:
2025-07-01 03:04:22.708 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:22.712 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:22.717 return
2025-07-01 03:04:22.721 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:22.726 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:22.731 else:
2025-07-01 03:04:22.735 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:22.740 eqi = None
2025-07-01 03:04:22.744
2025-07-01 03:04:22.749 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:22.753 # identical
2025-07-01 03:04:22.757
2025-07-01 03:04:22.762 # pump out diffs from before the synch point
2025-07-01 03:04:22.766 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:22.771
2025-07-01 03:04:22.776 # do intraline marking on the synch pair
2025-07-01 03:04:22.780 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:22.784 if eqi is None:
2025-07-01 03:04:22.789 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:22.793 atags = btags = ""
2025-07-01 03:04:22.798 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:22.802 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:22.806 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:22.811 if tag == 'replace':
2025-07-01 03:04:22.815 atags += '^' * la
2025-07-01 03:04:22.819 btags += '^' * lb
2025-07-01 03:04:22.824 elif tag == 'delete':
2025-07-01 03:04:22.828 atags += '-' * la
2025-07-01 03:04:22.833 elif tag == 'insert':
2025-07-01 03:04:22.837 btags += '+' * lb
2025-07-01 03:04:22.841 elif tag == 'equal':
2025-07-01 03:04:22.846 atags += ' ' * la
2025-07-01 03:04:22.850 btags += ' ' * lb
2025-07-01 03:04:22.854 else:
2025-07-01 03:04:22.859 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:22.863 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:22.867 else:
2025-07-01 03:04:22.872 # the synch pair is identical
2025-07-01 03:04:22.876 yield '  ' + aelt
2025-07-01 03:04:22.880
2025-07-01 03:04:22.885 # pump out diffs from after the synch point
2025-07-01 03:04:22.889 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:22.893
2025-07-01 03:04:22.897 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:22.902 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:22.906
2025-07-01 03:04:22.911 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:22.916 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:22.920 alo = 378, ahi = 1101
2025-07-01 03:04:22.925 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:22.930 blo = 378, bhi = 1101
2025-07-01 03:04:22.934
2025-07-01 03:04:22.938 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:22.943 g = []
2025-07-01 03:04:22.947 if alo < ahi:
2025-07-01 03:04:22.952 if blo < bhi:
2025-07-01 03:04:22.956 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:22.960 else:
2025-07-01 03:04:22.965 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:22.969 elif blo < bhi:
2025-07-01 03:04:22.974 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:22.978
2025-07-01 03:04:22.983 >       yield from g
2025-07-01 03:04:22.988
2025-07-01 03:04:22.992 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:22.997 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:23.002
2025-07-01 03:04:23.008 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:23.014 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:23.018 alo = 378, ahi = 1101
2025-07-01 03:04:23.023 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:23.027 blo = 378, bhi = 1101
2025-07-01 03:04:23.032
2025-07-01 03:04:23.036 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:23.041 r"""
2025-07-01 03:04:23.045 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:23.050 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:23.054 synch point, and intraline difference marking is done on the
2025-07-01 03:04:23.060 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:23.064
2025-07-01 03:04:23.068 Example:
2025-07-01 03:04:23.073
2025-07-01 03:04:23.077 >>> d = Differ()
2025-07-01 03:04:23.081 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:23.086 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:23.090 >>> print(''.join(results), end="")
2025-07-01 03:04:23.095 - abcDefghiJkl
2025-07-01 03:04:23.104 + abcdefGhijkl
2025-07-01 03:04:23.112 """
2025-07-01 03:04:23.117
2025-07-01 03:04:23.121 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:23.126 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:23.132 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:23.137 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:23.141 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:23.146
2025-07-01 03:04:23.151 # search for the pair that matches best without being identical
2025-07-01 03:04:23.155 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:23.160 # on junk -- unless we have to)
2025-07-01 03:04:23.165 for j in range(blo, bhi):
2025-07-01 03:04:23.170 bj = b[j]
2025-07-01 03:04:23.174 cruncher.set_seq2(bj)
2025-07-01 03:04:23.180 for i in range(alo, ahi):
2025-07-01 03:04:23.185 ai = a[i]
2025-07-01 03:04:23.190 if ai == bj:
2025-07-01 03:04:23.194 if eqi is None:
2025-07-01 03:04:23.198 eqi, eqj = i, j
2025-07-01 03:04:23.203 continue
2025-07-01 03:04:23.208 cruncher.set_seq1(ai)
2025-07-01 03:04:23.213 # computing similarity is expensive, so use the quick
2025-07-01 03:04:23.218 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:23.223 # compares by a factor of 3.
2025-07-01 03:04:23.228 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:23.233 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:23.237 # of the computation is cached by cruncher
2025-07-01 03:04:23.241 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:23.246 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:23.251 cruncher.ratio() > best_ratio:
2025-07-01 03:04:23.257 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:23.262 if best_ratio < cutoff:
2025-07-01 03:04:23.266 # no non-identical "pretty close" pair
2025-07-01 03:04:23.270 if eqi is None:
2025-07-01 03:04:23.275 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:23.279 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:23.283 return
2025-07-01 03:04:23.289 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:23.294 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:23.298 else:
2025-07-01 03:04:23.302 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:23.307 eqi = None
2025-07-01 03:04:23.311
2025-07-01 03:04:23.316 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:23.320 # identical
2025-07-01 03:04:23.324
2025-07-01 03:04:23.329 # pump out diffs from before the synch point
2025-07-01 03:04:23.333 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:23.338
2025-07-01 03:04:23.342 # do intraline marking on the synch pair
2025-07-01 03:04:23.347 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:23.352 if eqi is None:
2025-07-01 03:04:23.356 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:23.360 atags = btags = ""
2025-07-01 03:04:23.365 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:23.369 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:23.374 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:23.380 if tag == 'replace':
2025-07-01 03:04:23.388 atags += '^' * la
2025-07-01 03:04:23.394 btags += '^' * lb
2025-07-01 03:04:23.399 elif tag == 'delete':
2025-07-01 03:04:23.404 atags += '-' * la
2025-07-01 03:04:23.410 elif tag == 'insert':
2025-07-01 03:04:23.414 btags += '+' * lb
2025-07-01 03:04:23.419 elif tag == 'equal':
2025-07-01 03:04:23.424 atags += ' ' * la
2025-07-01 03:04:23.430 btags += ' ' * lb
2025-07-01 03:04:23.434 else:
2025-07-01 03:04:23.439 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:23.444 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:23.448 else:
2025-07-01 03:04:23.453 # the synch pair is identical
2025-07-01 03:04:23.458 yield '  ' + aelt
2025-07-01 03:04:23.462
2025-07-01 03:04:23.466 # pump out diffs from after the synch point
2025-07-01 03:04:23.471 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:23.477
2025-07-01 03:04:23.483 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:23.489 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:23.494
2025-07-01 03:04:23.500 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:23.506 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:23.512 alo = 379, ahi = 1101
2025-07-01 03:04:23.521 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:23.527 blo = 379, bhi = 1101
2025-07-01 03:04:23.532
2025-07-01 03:04:23.538 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:23.543 g = []
2025-07-01 03:04:23.549 if alo < ahi:
2025-07-01 03:04:23.555 if blo < bhi:
2025-07-01 03:04:23.561 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:23.567 else:
2025-07-01 03:04:23.573 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:23.578 elif blo < bhi:
2025-07-01 03:04:23.584 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:23.590
2025-07-01 03:04:23.596 >       yield from g
2025-07-01 03:04:23.601
2025-07-01 03:04:23.607 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:23.613 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:23.618
2025-07-01 03:04:23.624 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:23.630 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:23.636 alo = 379, ahi = 1101
2025-07-01 03:04:23.642 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:23.647 blo = 379, bhi = 1101
2025-07-01 03:04:23.653
2025-07-01 03:04:23.659 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:23.665 r"""
2025-07-01 03:04:23.674 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:23.683 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:23.692 synch point, and intraline difference marking is done on the
2025-07-01 03:04:23.698 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:23.703
2025-07-01 03:04:23.709 Example:
2025-07-01 03:04:23.715
2025-07-01 03:04:23.722 >>> d = Differ()
2025-07-01 03:04:23.733 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:23.743 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:23.749 >>> print(''.join(results), end="")
2025-07-01 03:04:23.754 - abcDefghiJkl
2025-07-01 03:04:23.763 + abcdefGhijkl
2025-07-01 03:04:23.771 """
2025-07-01 03:04:23.775
2025-07-01 03:04:23.780 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:23.784 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:23.789 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:23.794 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:23.798 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:23.805
2025-07-01 03:04:23.810 # search for the pair that matches best without being identical
2025-07-01 03:04:23.816 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:23.823 # on junk -- unless we have to)
2025-07-01 03:04:23.828 for j in range(blo, bhi):
2025-07-01 03:04:23.835 bj = b[j]
2025-07-01 03:04:23.841 cruncher.set_seq2(bj)
2025-07-01 03:04:23.847 for i in range(alo, ahi):
2025-07-01 03:04:23.852 ai = a[i]
2025-07-01 03:04:23.857 if ai == bj:
2025-07-01 03:04:23.861 if eqi is None:
2025-07-01 03:04:23.866 eqi, eqj = i, j
2025-07-01 03:04:23.870 continue
2025-07-01 03:04:23.875 cruncher.set_seq1(ai)
2025-07-01 03:04:23.879 # computing similarity is expensive, so use the quick
2025-07-01 03:04:23.884 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:23.888 # compares by a factor of 3.
2025-07-01 03:04:23.895 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:23.899 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:23.904 # of the computation is cached by cruncher
2025-07-01 03:04:23.908 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:23.913 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:23.917 cruncher.ratio() > best_ratio:
2025-07-01 03:04:23.923 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:23.927 if best_ratio < cutoff:
2025-07-01 03:04:23.932 # no non-identical "pretty close" pair
2025-07-01 03:04:23.936 if eqi is None:
2025-07-01 03:04:23.941 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:23.947 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:23.952 return
2025-07-01 03:04:23.956 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:23.961 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:23.965 else:
2025-07-01 03:04:23.969 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:23.974 eqi = None
2025-07-01 03:04:23.978
2025-07-01 03:04:23.982 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:23.987 # identical
2025-07-01 03:04:23.991
2025-07-01 03:04:23.995 # pump out diffs from before the synch point
2025-07-01 03:04:24.001 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:24.006
2025-07-01 03:04:24.012 # do intraline marking on the synch pair
2025-07-01 03:04:24.017 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:24.023 if eqi is None:
2025-07-01 03:04:24.027 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:24.031 atags = btags = ""
2025-07-01 03:04:24.036 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:24.040 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:24.046 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:24.051 if tag == 'replace':
2025-07-01 03:04:24.056 atags += '^' * la
2025-07-01 03:04:24.060 btags += '^' * lb
2025-07-01 03:04:24.064 elif tag == 'delete':
2025-07-01 03:04:24.068 atags += '-' * la
2025-07-01 03:04:24.073 elif tag == 'insert':
2025-07-01 03:04:24.078 btags += '+' * lb
2025-07-01 03:04:24.083 elif tag == 'equal':
2025-07-01 03:04:24.087 atags += ' ' * la
2025-07-01 03:04:24.094 btags += ' ' * lb
2025-07-01 03:04:24.098 else:
2025-07-01 03:04:24.102 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:24.108 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:24.113 else:
2025-07-01 03:04:24.117 # the synch pair is identical
2025-07-01 03:04:24.122 yield '  ' + aelt
2025-07-01 03:04:24.126
2025-07-01 03:04:24.131 # pump out diffs from after the synch point
2025-07-01 03:04:24.135 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:24.139
2025-07-01 03:04:24.144 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:24.148 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:24.153
2025-07-01 03:04:24.157 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:24.162 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:24.166 alo = 380, ahi = 1101
2025-07-01 03:04:24.171 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:24.176 blo = 380, bhi = 1101
2025-07-01 03:04:24.180
2025-07-01 03:04:24.184 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:24.188 g = []
2025-07-01 03:04:24.193 if alo < ahi:
2025-07-01 03:04:24.197 if blo < bhi:
2025-07-01 03:04:24.202 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:24.208 else:
2025-07-01 03:04:24.212 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:24.217 elif blo < bhi:
2025-07-01 03:04:24.221 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:24.225
2025-07-01 03:04:24.229 >       yield from g
2025-07-01 03:04:24.234
2025-07-01 03:04:24.238 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:24.242 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:24.247
2025-07-01 03:04:24.251 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:24.256 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:24.260 alo = 380, ahi = 1101
2025-07-01 03:04:24.265 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:24.269 blo = 380, bhi = 1101
2025-07-01 03:04:24.273
2025-07-01 03:04:24.278 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:24.283 r"""
2025-07-01 03:04:24.288 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:24.292 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:24.296 synch point, and intraline difference marking is done on the
2025-07-01 03:04:24.301 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:24.305
2025-07-01 03:04:24.309 Example:
2025-07-01 03:04:24.314
2025-07-01 03:04:24.318 >>> d = Differ()
2025-07-01 03:04:24.322 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:24.327 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:24.332 >>> print(''.join(results), end="")
2025-07-01 03:04:24.336 - abcDefghiJkl
2025-07-01 03:04:24.345 + abcdefGhijkl
2025-07-01 03:04:24.353 """
2025-07-01 03:04:24.358
2025-07-01 03:04:24.362 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:24.368 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:24.373 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:24.377 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:24.382 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:24.386
2025-07-01 03:04:24.391 # search for the pair that matches best without being identical
2025-07-01 03:04:24.395 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:24.399 # on junk -- unless we have to)
2025-07-01 03:04:24.404 for j in range(blo, bhi):
2025-07-01 03:04:24.408 bj = b[j]
2025-07-01 03:04:24.412 cruncher.set_seq2(bj)
2025-07-01 03:04:24.417 for i in range(alo, ahi):
2025-07-01 03:04:24.421 ai = a[i]
2025-07-01 03:04:24.425 if ai == bj:
2025-07-01 03:04:24.430 if eqi is None:
2025-07-01 03:04:24.434 eqi, eqj = i, j
2025-07-01 03:04:24.438 continue
2025-07-01 03:04:24.443 cruncher.set_seq1(ai)
2025-07-01 03:04:24.447 # computing similarity is expensive, so use the quick
2025-07-01 03:04:24.452 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:24.456 # compares by a factor of 3.
2025-07-01 03:04:24.461 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:24.465 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:24.469 # of the computation is cached by cruncher
2025-07-01 03:04:24.474 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:24.478 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:24.483 cruncher.ratio() > best_ratio:
2025-07-01 03:04:24.487 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:24.492 if best_ratio < cutoff:
2025-07-01 03:04:24.497 # no non-identical "pretty close" pair
2025-07-01 03:04:24.501 if eqi is None:
2025-07-01 03:04:24.506 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:24.510 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:24.516 return
2025-07-01 03:04:24.523 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:24.527 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:24.532 else:
2025-07-01 03:04:24.538 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:24.544 eqi = None
2025-07-01 03:04:24.549
2025-07-01 03:04:24.554 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:24.558 # identical
2025-07-01 03:04:24.562
2025-07-01 03:04:24.567 # pump out diffs from before the synch point
2025-07-01 03:04:24.571 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:24.576
2025-07-01 03:04:24.580 # do intraline marking on the synch pair
2025-07-01 03:04:24.585 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:24.589 if eqi is None:
2025-07-01 03:04:24.593 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:24.598 atags = btags = ""
2025-07-01 03:04:24.602 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:24.607 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:24.611 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:24.617 if tag == 'replace':
2025-07-01 03:04:24.623 atags += '^' * la
2025-07-01 03:04:24.628 btags += '^' * lb
2025-07-01 03:04:24.633 elif tag == 'delete':
2025-07-01 03:04:24.637 atags += '-' * la
2025-07-01 03:04:24.642 elif tag == 'insert':
2025-07-01 03:04:24.646 btags += '+' * lb
2025-07-01 03:04:24.650 elif tag == 'equal':
2025-07-01 03:04:24.655 atags += ' ' * la
2025-07-01 03:04:24.659 btags += ' ' * lb
2025-07-01 03:04:24.663 else:
2025-07-01 03:04:24.668 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:24.672 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:24.679 else:
2025-07-01 03:04:24.683 # the synch pair is identical
2025-07-01 03:04:24.688 yield '  ' + aelt
2025-07-01 03:04:24.692
2025-07-01 03:04:24.697 # pump out diffs from after the synch point
2025-07-01 03:04:24.702 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:24.709
2025-07-01 03:04:24.714 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:24.718 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:24.723
2025-07-01 03:04:24.729 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:24.734 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:24.738 alo = 381, ahi = 1101
2025-07-01 03:04:24.743 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:24.747 blo = 381, bhi = 1101
2025-07-01 03:04:24.751
2025-07-01 03:04:24.756 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:24.760 g = []
2025-07-01 03:04:24.764 if alo < ahi:
2025-07-01 03:04:24.769 if blo < bhi:
2025-07-01 03:04:24.773 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:24.777 else:
2025-07-01 03:04:24.782 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:24.786 elif blo < bhi:
2025-07-01 03:04:24.791 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:24.795
2025-07-01 03:04:24.799 >       yield from g
2025-07-01 03:04:24.803
2025-07-01 03:04:24.810 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:24.814 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:24.818
2025-07-01 03:04:24.824 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:24.828 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:24.833 alo = 381, ahi = 1101
2025-07-01 03:04:24.838 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:24.842 blo = 381, bhi = 1101
2025-07-01 03:04:24.847
2025-07-01 03:04:24.851 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:24.855 r"""
2025-07-01 03:04:24.860 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:24.864 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:24.869 synch point, and intraline difference marking is done on the
2025-07-01 03:04:24.873 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:24.878
2025-07-01 03:04:24.882 Example:
2025-07-01 03:04:24.886
2025-07-01 03:04:24.891 >>> d = Differ()
2025-07-01 03:04:24.895 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:24.900 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:24.904 >>> print(''.join(results), end="")
2025-07-01 03:04:24.909 - abcDefghiJkl
2025-07-01 03:04:24.919 + abcdefGhijkl
2025-07-01 03:04:24.930 """
2025-07-01 03:04:24.934
2025-07-01 03:04:24.939 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:24.944 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:24.948 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:24.953 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:24.958 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:24.964
2025-07-01 03:04:24.968 # search for the pair that matches best without being identical
2025-07-01 03:04:24.973 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:24.978 # on junk -- unless we have to)
2025-07-01 03:04:24.982 for j in range(blo, bhi):
2025-07-01 03:04:24.987 bj = b[j]
2025-07-01 03:04:24.991 cruncher.set_seq2(bj)
2025-07-01 03:04:24.996 for i in range(alo, ahi):
2025-07-01 03:04:25.000 ai = a[i]
2025-07-01 03:04:25.005 if ai == bj:
2025-07-01 03:04:25.010 if eqi is None:
2025-07-01 03:04:25.015 eqi, eqj = i, j
2025-07-01 03:04:25.021 continue
2025-07-01 03:04:25.026 cruncher.set_seq1(ai)
2025-07-01 03:04:25.032 # computing similarity is expensive, so use the quick
2025-07-01 03:04:25.037 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:25.042 # compares by a factor of 3.
2025-07-01 03:04:25.047 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:25.053 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:25.058 # of the computation is cached by cruncher
2025-07-01 03:04:25.063 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:25.068 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:25.073 cruncher.ratio() > best_ratio:
2025-07-01 03:04:25.077 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:25.082 if best_ratio < cutoff:
2025-07-01 03:04:25.088 # no non-identical "pretty close" pair
2025-07-01 03:04:25.093 if eqi is None:
2025-07-01 03:04:25.098 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:25.102 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:25.107 return
2025-07-01 03:04:25.111 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:25.117 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:25.121 else:
2025-07-01 03:04:25.126 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:25.130 eqi = None
2025-07-01 03:04:25.135
2025-07-01 03:04:25.140 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:25.144 # identical
2025-07-01 03:04:25.148
2025-07-01 03:04:25.153 # pump out diffs from before the synch point
2025-07-01 03:04:25.162 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:25.167
2025-07-01 03:04:25.171 # do intraline marking on the synch pair
2025-07-01 03:04:25.176 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:25.180 if eqi is None:
2025-07-01 03:04:25.185 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:25.189 atags = btags = ""
2025-07-01 03:04:25.195 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:25.199 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:25.204 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:25.208 if tag == 'replace':
2025-07-01 03:04:25.213 atags += '^' * la
2025-07-01 03:04:25.218 btags += '^' * lb
2025-07-01 03:04:25.222 elif tag == 'delete':
2025-07-01 03:04:25.227 atags += '-' * la
2025-07-01 03:04:25.231 elif tag == 'insert':
2025-07-01 03:04:25.235 btags += '+' * lb
2025-07-01 03:04:25.240 elif tag == 'equal':
2025-07-01 03:04:25.244 atags += ' ' * la
2025-07-01 03:04:25.249 btags += ' ' * lb
2025-07-01 03:04:25.253 else:
2025-07-01 03:04:25.257 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:25.262 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:25.266 else:
2025-07-01 03:04:25.271 # the synch pair is identical
2025-07-01 03:04:25.275 yield '  ' + aelt
2025-07-01 03:04:25.280
2025-07-01 03:04:25.285 # pump out diffs from after the synch point
2025-07-01 03:04:25.289 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:25.295
2025-07-01 03:04:25.300 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:25.305 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:25.309
2025-07-01 03:04:25.314 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:25.319 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:25.323 alo = 382, ahi = 1101
2025-07-01 03:04:25.328 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:25.333 blo = 382, bhi = 1101
2025-07-01 03:04:25.337
2025-07-01 03:04:25.341 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:25.346 g = []
2025-07-01 03:04:25.350 if alo < ahi:
2025-07-01 03:04:25.355 if blo < bhi:
2025-07-01 03:04:25.360 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:25.366 else:
2025-07-01 03:04:25.374 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:25.379 elif blo < bhi:
2025-07-01 03:04:25.383 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:25.389
2025-07-01 03:04:25.398 >       yield from g
2025-07-01 03:04:25.404
2025-07-01 03:04:25.409 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:25.413 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:25.418
2025-07-01 03:04:25.422 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:25.430 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:25.441 alo = 382, ahi = 1101
2025-07-01 03:04:25.453 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:25.462 blo = 382, bhi = 1101
2025-07-01 03:04:25.471
2025-07-01 03:04:25.482 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:25.493 r"""
2025-07-01 03:04:25.505 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:25.517 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:25.525 synch point, and intraline difference marking is done on the
2025-07-01 03:04:25.537 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:25.552
2025-07-01 03:04:25.557 Example:
2025-07-01 03:04:25.570
2025-07-01 03:04:25.577 >>> d = Differ()
2025-07-01 03:04:25.591 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:25.606 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:25.614 >>> print(''.join(results), end="")
2025-07-01 03:04:25.626 - abcDefghiJkl
2025-07-01 03:04:25.646 + abcdefGhijkl
2025-07-01 03:04:25.666 """
2025-07-01 03:04:25.674
2025-07-01 03:04:25.686 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:25.699 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:25.714 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:25.724 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:25.742 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:25.750
2025-07-01 03:04:25.765 # search for the pair that matches best without being identical
2025-07-01 03:04:25.782 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:25.796 # on junk -- unless we have to)
2025-07-01 03:04:25.809 for j in range(blo, bhi):
2025-07-01 03:04:25.822 bj = b[j]
2025-07-01 03:04:25.833 cruncher.set_seq2(bj)
2025-07-01 03:04:25.850 for i in range(alo, ahi):
2025-07-01 03:04:25.866 ai = a[i]
2025-07-01 03:04:25.881 if ai == bj:
2025-07-01 03:04:25.895 if eqi is None:
2025-07-01 03:04:25.906 eqi, eqj = i, j
2025-07-01 03:04:25.913 continue
2025-07-01 03:04:25.925 cruncher.set_seq1(ai)
2025-07-01 03:04:25.941 # computing similarity is expensive, so use the quick
2025-07-01 03:04:25.953 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:25.966 # compares by a factor of 3.
2025-07-01 03:04:25.978 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:25.988 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:25.998 # of the computation is cached by cruncher
2025-07-01 03:04:26.005 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:26.018 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:26.032 cruncher.ratio() > best_ratio:
2025-07-01 03:04:26.045 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:26.049 if best_ratio < cutoff:
2025-07-01 03:04:26.056 # no non-identical "pretty close" pair
2025-07-01 03:04:26.063 if eqi is None:
2025-07-01 03:04:26.068 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:26.073 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:26.078 return
2025-07-01 03:04:26.082 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:26.087 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:26.092 else:
2025-07-01 03:04:26.096 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:26.101 eqi = None
2025-07-01 03:04:26.105
2025-07-01 03:04:26.110 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:26.114 # identical
2025-07-01 03:04:26.119
2025-07-01 03:04:26.123 # pump out diffs from before the synch point
2025-07-01 03:04:26.128 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:26.132
2025-07-01 03:04:26.137 # do intraline marking on the synch pair
2025-07-01 03:04:26.142 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:26.146 if eqi is None:
2025-07-01 03:04:26.151 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:26.156 atags = btags = ""
2025-07-01 03:04:26.161 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:26.166 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:26.171 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:26.176 if tag == 'replace':
2025-07-01 03:04:26.181 atags += '^' * la
2025-07-01 03:04:26.185 btags += '^' * lb
2025-07-01 03:04:26.191 elif tag == 'delete':
2025-07-01 03:04:26.196 atags += '-' * la
2025-07-01 03:04:26.200 elif tag == 'insert':
2025-07-01 03:04:26.205 btags += '+' * lb
2025-07-01 03:04:26.209 elif tag == 'equal':
2025-07-01 03:04:26.214 atags += ' ' * la
2025-07-01 03:04:26.219 btags += ' ' * lb
2025-07-01 03:04:26.224 else:
2025-07-01 03:04:26.229 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:26.233 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:26.237 else:
2025-07-01 03:04:26.242 # the synch pair is identical
2025-07-01 03:04:26.246 yield '  ' + aelt
2025-07-01 03:04:26.251
2025-07-01 03:04:26.256 # pump out diffs from after the synch point
2025-07-01 03:04:26.260 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:26.265
2025-07-01 03:04:26.269 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:26.274 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:26.279
2025-07-01 03:04:26.284 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:26.289 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:26.294 alo = 383, ahi = 1101
2025-07-01 03:04:26.299 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:26.303 blo = 383, bhi = 1101
2025-07-01 03:04:26.308
2025-07-01 03:04:26.312 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:26.317 g = []
2025-07-01 03:04:26.321 if alo < ahi:
2025-07-01 03:04:26.326 if blo < bhi:
2025-07-01 03:04:26.330 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:26.335 else:
2025-07-01 03:04:26.340 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:26.345 elif blo < bhi:
2025-07-01 03:04:26.349 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:26.354
2025-07-01 03:04:26.359 >       yield from g
2025-07-01 03:04:26.363
2025-07-01 03:04:26.367 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:26.372 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:26.377
2025-07-01 03:04:26.381 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:26.387 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:26.391 alo = 383, ahi = 1101
2025-07-01 03:04:26.396 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:26.401 blo = 383, bhi = 1101
2025-07-01 03:04:26.406
2025-07-01 03:04:26.410 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:26.415 r"""
2025-07-01 03:04:26.420 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:26.425 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:26.429 synch point, and intraline difference marking is done on the
2025-07-01 03:04:26.434 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:26.439
2025-07-01 03:04:26.443 Example:
2025-07-01 03:04:26.447
2025-07-01 03:04:26.452 >>> d = Differ()
2025-07-01 03:04:26.456 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:26.461 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:26.466 >>> print(''.join(results), end="")
2025-07-01 03:04:26.470 - abcDefghiJkl
2025-07-01 03:04:26.479 + abcdefGhijkl
2025-07-01 03:04:26.488 """
2025-07-01 03:04:26.493
2025-07-01 03:04:26.497 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:26.502 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:26.507 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:26.512 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:26.516 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:26.520
2025-07-01 03:04:26.525 # search for the pair that matches best without being identical
2025-07-01 03:04:26.529 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:26.533 # on junk -- unless we have to)
2025-07-01 03:04:26.538 for j in range(blo, bhi):
2025-07-01 03:04:26.542 bj = b[j]
2025-07-01 03:04:26.547 cruncher.set_seq2(bj)
2025-07-01 03:04:26.551 for i in range(alo, ahi):
2025-07-01 03:04:26.556 ai = a[i]
2025-07-01 03:04:26.560 if ai == bj:
2025-07-01 03:04:26.565 if eqi is None:
2025-07-01 03:04:26.569 eqi, eqj = i, j
2025-07-01 03:04:26.574 continue
2025-07-01 03:04:26.578 cruncher.set_seq1(ai)
2025-07-01 03:04:26.583 # computing similarity is expensive, so use the quick
2025-07-01 03:04:26.588 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:26.592 # compares by a factor of 3.
2025-07-01 03:04:26.597 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:26.602 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:26.606 # of the computation is cached by cruncher
2025-07-01 03:04:26.611 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:26.615 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:26.620 cruncher.ratio() > best_ratio:
2025-07-01 03:04:26.624 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:26.629 if best_ratio < cutoff:
2025-07-01 03:04:26.634 # no non-identical "pretty close" pair
2025-07-01 03:04:26.638 if eqi is None:
2025-07-01 03:04:26.643 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:26.648 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:26.653 return
2025-07-01 03:04:26.657 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:26.662 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:26.667 else:
2025-07-01 03:04:26.672 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:26.676 eqi = None
2025-07-01 03:04:26.681
2025-07-01 03:04:26.686 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:26.691 # identical
2025-07-01 03:04:26.695
2025-07-01 03:04:26.700 # pump out diffs from before the synch point
2025-07-01 03:04:26.704 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:26.708
2025-07-01 03:04:26.713 # do intraline marking on the synch pair
2025-07-01 03:04:26.717 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:26.722 if eqi is None:
2025-07-01 03:04:26.726 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:26.731 atags = btags = ""
2025-07-01 03:04:26.735 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:26.740 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:26.744 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:26.749 if tag == 'replace':
2025-07-01 03:04:26.753 atags += '^' * la
2025-07-01 03:04:26.758 btags += '^' * lb
2025-07-01 03:04:26.762 elif tag == 'delete':
2025-07-01 03:04:26.767 atags += '-' * la
2025-07-01 03:04:26.771 elif tag == 'insert':
2025-07-01 03:04:26.776 btags += '+' * lb
2025-07-01 03:04:26.781 elif tag == 'equal':
2025-07-01 03:04:26.785 atags += ' ' * la
2025-07-01 03:04:26.790 btags += ' ' * lb
2025-07-01 03:04:26.794 else:
2025-07-01 03:04:26.799 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:26.804 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:26.809 else:
2025-07-01 03:04:26.813 # the synch pair is identical
2025-07-01 03:04:26.818 yield '  ' + aelt
2025-07-01 03:04:26.822
2025-07-01 03:04:26.827 # pump out diffs from after the synch point
2025-07-01 03:04:26.833 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:26.839
2025-07-01 03:04:26.845 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:26.852 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:26.859
2025-07-01 03:04:26.866 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:26.873 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:26.878 alo = 384, ahi = 1101
2025-07-01 03:04:26.885 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:26.890 blo = 384, bhi = 1101
2025-07-01 03:04:26.895
2025-07-01 03:04:26.900 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:26.904 g = []
2025-07-01 03:04:26.909 if alo < ahi:
2025-07-01 03:04:26.914 if blo < bhi:
2025-07-01 03:04:26.919 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:26.925 else:
2025-07-01 03:04:26.929 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:26.934 elif blo < bhi:
2025-07-01 03:04:26.939 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:26.943
2025-07-01 03:04:26.948 >       yield from g
2025-07-01 03:04:26.953
2025-07-01 03:04:26.958 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:26.963 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:26.967
2025-07-01 03:04:26.972 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:26.977 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:26.981 alo = 384, ahi = 1101
2025-07-01 03:04:26.986 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:26.991 blo = 384, bhi = 1101
2025-07-01 03:04:26.995
2025-07-01 03:04:27.000 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:27.004 r"""
2025-07-01 03:04:27.009 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:27.014 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:27.019 synch point, and intraline difference marking is done on the
2025-07-01 03:04:27.023 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:27.028
2025-07-01 03:04:27.032 Example:
2025-07-01 03:04:27.037
2025-07-01 03:04:27.041 >>> d = Differ()
2025-07-01 03:04:27.046 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:27.051 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:27.055 >>> print(''.join(results), end="")
2025-07-01 03:04:27.060 - abcDefghiJkl
2025-07-01 03:04:27.069 + abcdefGhijkl
2025-07-01 03:04:27.077 """
2025-07-01 03:04:27.082
2025-07-01 03:04:27.086 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:27.091 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:27.095 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:27.100 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:27.105 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:27.110
2025-07-01 03:04:27.114 # search for the pair that matches best without being identical
2025-07-01 03:04:27.119 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:27.123 # on junk -- unless we have to)
2025-07-01 03:04:27.128 for j in range(blo, bhi):
2025-07-01 03:04:27.132 bj = b[j]
2025-07-01 03:04:27.137 cruncher.set_seq2(bj)
2025-07-01 03:04:27.141 for i in range(alo, ahi):
2025-07-01 03:04:27.146 ai = a[i]
2025-07-01 03:04:27.150 if ai == bj:
2025-07-01 03:04:27.155 if eqi is None:
2025-07-01 03:04:27.159 eqi, eqj = i, j
2025-07-01 03:04:27.163 continue
2025-07-01 03:04:27.168 cruncher.set_seq1(ai)
2025-07-01 03:04:27.172 # computing similarity is expensive, so use the quick
2025-07-01 03:04:27.177 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:27.181 # compares by a factor of 3.
2025-07-01 03:04:27.194 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:27.202 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:27.210 # of the computation is cached by cruncher
2025-07-01 03:04:27.226 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:27.238 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:27.250 cruncher.ratio() > best_ratio:
2025-07-01 03:04:27.258 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:27.271 if best_ratio < cutoff:
2025-07-01 03:04:27.282 # no non-identical "pretty close" pair
2025-07-01 03:04:27.290 if eqi is None:
2025-07-01 03:04:27.306 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:27.313 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:27.326 return
2025-07-01 03:04:27.338 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:27.345 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:27.358 else:
2025-07-01 03:04:27.370 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:27.378 eqi = None
2025-07-01 03:04:27.388
2025-07-01 03:04:27.399 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:27.418 # identical
2025-07-01 03:04:27.430
2025-07-01 03:04:27.438 # pump out diffs from before the synch point
2025-07-01 03:04:27.454 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:27.462
2025-07-01 03:04:27.470 # do intraline marking on the synch pair
2025-07-01 03:04:27.483 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:27.494 if eqi is None:
2025-07-01 03:04:27.506 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:27.518 atags = btags = ""
2025-07-01 03:04:27.534 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:27.541 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:27.550 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:27.562 if tag == 'replace':
2025-07-01 03:04:27.572 atags += '^' * la
2025-07-01 03:04:27.586 btags += '^' * lb
2025-07-01 03:04:27.598 elif tag == 'delete':
2025-07-01 03:04:27.606 atags += '-' * la
2025-07-01 03:04:27.618 elif tag == 'insert':
2025-07-01 03:04:27.625 btags += '+' * lb
2025-07-01 03:04:27.642 elif tag == 'equal':
2025-07-01 03:04:27.650 atags += ' ' * la
2025-07-01 03:04:27.662 btags += ' ' * lb
2025-07-01 03:04:27.670 else:
2025-07-01 03:04:27.682 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:27.690 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:27.702 else:
2025-07-01 03:04:27.710 # the synch pair is identical
2025-07-01 03:04:27.722 yield '  ' + aelt
2025-07-01 03:04:27.730
2025-07-01 03:04:27.742 # pump out diffs from after the synch point
2025-07-01 03:04:27.754 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:27.769
2025-07-01 03:04:27.780 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:27.798 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:27.806
2025-07-01 03:04:27.818 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:27.830 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:27.842 alo = 385, ahi = 1101
2025-07-01 03:04:27.851 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:27.866 blo = 385, bhi = 1101
2025-07-01 03:04:27.874
2025-07-01 03:04:27.890 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:27.898 g = []
2025-07-01 03:04:27.909 if alo < ahi:
2025-07-01 03:04:27.920 if blo < bhi:
2025-07-01 03:04:27.926 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:27.931 else:
2025-07-01 03:04:27.937 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:27.943 elif blo < bhi:
2025-07-01 03:04:27.949 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:27.955
2025-07-01 03:04:27.960 >       yield from g
2025-07-01 03:04:27.966
2025-07-01 03:04:27.970 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:27.976 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:27.983
2025-07-01 03:04:27.988 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:27.994 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:28.000 alo = 385, ahi = 1101
2025-07-01 03:04:28.007 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:28.013 blo = 385, bhi = 1101
2025-07-01 03:04:28.019
2025-07-01 03:04:28.026 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:28.034 r"""
2025-07-01 03:04:28.044 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:28.049 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:28.053 synch point, and intraline difference marking is done on the
2025-07-01 03:04:28.058 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:28.062
2025-07-01 03:04:28.067 Example:
2025-07-01 03:04:28.082
2025-07-01 03:04:28.090 >>> d = Differ()
2025-07-01 03:04:28.102 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:28.110 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:28.126 >>> print(''.join(results), end="")
2025-07-01 03:04:28.132 - abcDefghiJkl
2025-07-01 03:04:28.158 + abcdefGhijkl
2025-07-01 03:04:28.182 """
2025-07-01 03:04:28.198
2025-07-01 03:04:28.210 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:28.222 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:28.234 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:28.242 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:28.254 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:28.270
2025-07-01 03:04:28.280 # search for the pair that matches best without being identical
2025-07-01 03:04:28.298 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:28.306 # on junk -- unless we have to)
2025-07-01 03:04:28.323 for j in range(blo, bhi):
2025-07-01 03:04:28.334 bj = b[j]
2025-07-01 03:04:28.346 cruncher.set_seq2(bj)
2025-07-01 03:04:28.357 for i in range(alo, ahi):
2025-07-01 03:04:28.369 ai = a[i]
2025-07-01 03:04:28.379 if ai == bj:
2025-07-01 03:04:28.395 if eqi is None:
2025-07-01 03:04:28.411 eqi, eqj = i, j
2025-07-01 03:04:28.419 continue
2025-07-01 03:04:28.433 cruncher.set_seq1(ai)
2025-07-01 03:04:28.447 # computing similarity is expensive, so use the quick
2025-07-01 03:04:28.458 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:28.466 # compares by a factor of 3.
2025-07-01 03:04:28.478 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:28.490 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:28.505 # of the computation is cached by cruncher
2025-07-01 03:04:28.518 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:28.526 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:28.541 cruncher.ratio() > best_ratio:
2025-07-01 03:04:28.550 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:28.559 if best_ratio < cutoff:
2025-07-01 03:04:28.574 # no non-identical "pretty close" pair
2025-07-01 03:04:28.582 if eqi is None:
2025-07-01 03:04:28.598 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:28.612 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:28.630 return
2025-07-01 03:04:28.642 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:28.652 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:28.670 else:
2025-07-01 03:04:28.681 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:28.698 eqi = None
2025-07-01 03:04:28.706
2025-07-01 03:04:28.718 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:28.726 # identical
2025-07-01 03:04:28.738
2025-07-01 03:04:28.746 # pump out diffs from before the synch point
2025-07-01 03:04:28.758 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:28.766
2025-07-01 03:04:28.778 # do intraline marking on the synch pair
2025-07-01 03:04:28.790 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:28.798 if eqi is None:
2025-07-01 03:04:28.806 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:28.816 atags = btags = ""
2025-07-01 03:04:28.830 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:28.842 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:28.854 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:28.862 if tag == 'replace':
2025-07-01 03:04:28.874 atags += '^' * la
2025-07-01 03:04:28.886 btags += '^' * lb
2025-07-01 03:04:28.898 elif tag == 'delete':
2025-07-01 03:04:28.910 atags += '-' * la
2025-07-01 03:04:28.922 elif tag == 'insert':
2025-07-01 03:04:28.930 btags += '+' * lb
2025-07-01 03:04:28.942 elif tag == 'equal':
2025-07-01 03:04:28.950 atags += ' ' * la
2025-07-01 03:04:28.962 btags += ' ' * lb
2025-07-01 03:04:28.974 else:
2025-07-01 03:04:28.986 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:28.994 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:29.010 else:
2025-07-01 03:04:29.019 # the synch pair is identical
2025-07-01 03:04:29.034 yield '  ' + aelt
2025-07-01 03:04:29.044
2025-07-01 03:04:29.055 # pump out diffs from after the synch point
2025-07-01 03:04:29.070 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:29.082
2025-07-01 03:04:29.090 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:29.102 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:29.114
2025-07-01 03:04:29.121 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:29.137 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:29.150 alo = 386, ahi = 1101
2025-07-01 03:04:29.160 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:29.173 blo = 386, bhi = 1101
2025-07-01 03:04:29.190
2025-07-01 03:04:29.198 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:29.213 g = []
2025-07-01 03:04:29.226 if alo < ahi:
2025-07-01 03:04:29.237 if blo < bhi:
2025-07-01 03:04:29.253 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:29.266 else:
2025-07-01 03:04:29.280 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:29.294 elif blo < bhi:
2025-07-01 03:04:29.306 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:29.314
2025-07-01 03:04:29.326 >       yield from g
2025-07-01 03:04:29.334
2025-07-01 03:04:29.345 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:29.354 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:29.366
2025-07-01 03:04:29.373 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:29.388 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:29.401 alo = 386, ahi = 1101
2025-07-01 03:04:29.415 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:29.434 blo = 386, bhi = 1101
2025-07-01 03:04:29.454
2025-07-01 03:04:29.462 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:29.474 r"""
2025-07-01 03:04:29.481 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:29.493 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:29.505 synch point, and intraline difference marking is done on the
2025-07-01 03:04:29.514 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:29.526
2025-07-01 03:04:29.533 Example:
2025-07-01 03:04:29.545
2025-07-01 03:04:29.553 >>> d = Differ()
2025-07-01 03:04:29.566 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:29.579 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:29.586 >>> print(''.join(results), end="")
2025-07-01 03:04:29.593 - abcDefghiJkl
2025-07-01 03:04:29.618 + abcdefGhijkl
2025-07-01 03:04:29.638 """
2025-07-01 03:04:29.646
2025-07-01 03:04:29.658 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:29.670 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:29.678 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:29.694 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:29.706 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:29.721
2025-07-01 03:04:29.730 # search for the pair that matches best without being identical
2025-07-01 03:04:29.738 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:29.750 # on junk -- unless we have to)
2025-07-01 03:04:29.758 for j in range(blo, bhi):
2025-07-01 03:04:29.770 bj = b[j]
2025-07-01 03:04:29.786 cruncher.set_seq2(bj)
2025-07-01 03:04:29.802 for i in range(alo, ahi):
2025-07-01 03:04:29.814 ai = a[i]
2025-07-01 03:04:29.822 if ai == bj:
2025-07-01 03:04:29.830 if eqi is None:
2025-07-01 03:04:29.842 eqi, eqj = i, j
2025-07-01 03:04:29.854 continue
2025-07-01 03:04:29.864 cruncher.set_seq1(ai)
2025-07-01 03:04:29.880 # computing similarity is expensive, so use the quick
2025-07-01 03:04:29.890 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:29.902 # compares by a factor of 3.
2025-07-01 03:04:29.914 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:29.922 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:29.932 # of the computation is cached by cruncher
2025-07-01 03:04:29.941 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:29.949 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:29.968 cruncher.ratio() > best_ratio:
2025-07-01 03:04:29.982 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:29.990 if best_ratio < cutoff:
2025-07-01 03:04:30.004 # no non-identical "pretty close" pair
2025-07-01 03:04:30.014 if eqi is None:
2025-07-01 03:04:30.026 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:30.034 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:30.046 return
2025-07-01 03:04:30.054 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:30.066 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:30.073 else:
2025-07-01 03:04:30.090 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:30.100 eqi = None
2025-07-01 03:04:30.110
2025-07-01 03:04:30.122 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:30.130 # identical
2025-07-01 03:04:30.137
2025-07-01 03:04:30.154 # pump out diffs from before the synch point
2025-07-01 03:04:30.166 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:30.182
2025-07-01 03:04:30.194 # do intraline marking on the synch pair
2025-07-01 03:04:30.202 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:30.218 if eqi is None:
2025-07-01 03:04:30.225 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:30.239 atags = btags = ""
2025-07-01 03:04:30.254 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:30.262 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:30.278 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:30.288 if tag == 'replace':
2025-07-01 03:04:30.305 atags += '^' * la
2025-07-01 03:04:30.315 btags += '^' * lb
2025-07-01 03:04:30.330 elif tag == 'delete':
2025-07-01 03:04:30.338 atags += '-' * la
2025-07-01 03:04:30.350 elif tag == 'insert':
2025-07-01 03:04:30.362 btags += '+' * lb
2025-07-01 03:04:30.374 elif tag == 'equal':
2025-07-01 03:04:30.382 atags += ' ' * la
2025-07-01 03:04:30.390 btags += ' ' * lb
2025-07-01 03:04:30.402 else:
2025-07-01 03:04:30.418 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:30.430 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:30.442 else:
2025-07-01 03:04:30.450 # the synch pair is identical
2025-07-01 03:04:30.459 yield '  ' + aelt
2025-07-01 03:04:30.465
2025-07-01 03:04:30.477 # pump out diffs from after the synch point
2025-07-01 03:04:30.494 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:30.502
2025-07-01 03:04:30.514 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:30.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:30.534
2025-07-01 03:04:30.546 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:30.558 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:30.574 alo = 387, ahi = 1101
2025-07-01 03:04:30.590 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:30.597 blo = 387, bhi = 1101
2025-07-01 03:04:30.606
2025-07-01 03:04:30.619 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:30.626 g = []
2025-07-01 03:04:30.634 if alo < ahi:
2025-07-01 03:04:30.650 if blo < bhi:
2025-07-01 03:04:30.662 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:30.674 else:
2025-07-01 03:04:30.686 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:30.698 elif blo < bhi:
2025-07-01 03:04:30.706 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:30.718
2025-07-01 03:04:30.726 >       yield from g
2025-07-01 03:04:30.742
2025-07-01 03:04:30.750 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:30.758 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:30.765
2025-07-01 03:04:30.776 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:30.798 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:30.806 alo = 387, ahi = 1101
2025-07-01 03:04:30.826 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:30.838 blo = 387, bhi = 1101
2025-07-01 03:04:30.850
2025-07-01 03:04:30.866 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:30.881 r"""
2025-07-01 03:04:30.894 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:30.909 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:30.921 synch point, and intraline difference marking is done on the
2025-07-01 03:04:30.937 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:30.953
2025-07-01 03:04:30.958 Example:
2025-07-01 03:04:30.971
2025-07-01 03:04:30.983 >>> d = Differ()
2025-07-01 03:04:30.998 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:31.009 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:31.025 >>> print(''.join(results), end="")
2025-07-01 03:04:31.030 - abcDefghiJkl
2025-07-01 03:04:31.054 + abcdefGhijkl
2025-07-01 03:04:31.078 """
2025-07-01 03:04:31.090
2025-07-01 03:04:31.098 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:31.110 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:31.118 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:31.130 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:31.142 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:31.154
2025-07-01 03:04:31.166 # search for the pair that matches best without being identical
2025-07-01 03:04:31.174 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:31.186 # on junk -- unless we have to)
2025-07-01 03:04:31.198 for j in range(blo, bhi):
2025-07-01 03:04:31.206 bj = b[j]
2025-07-01 03:04:31.218 cruncher.set_seq2(bj)
2025-07-01 03:04:31.226 for i in range(alo, ahi):
2025-07-01 03:04:31.238 ai = a[i]
2025-07-01 03:04:31.246 if ai == bj:
2025-07-01 03:04:31.258 if eqi is None:
2025-07-01 03:04:31.270 eqi, eqj = i, j
2025-07-01 03:04:31.278 continue
2025-07-01 03:04:31.290 cruncher.set_seq1(ai)
2025-07-01 03:04:31.302 # computing similarity is expensive, so use the quick
2025-07-01 03:04:31.310 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:31.322 # compares by a factor of 3.
2025-07-01 03:04:31.338 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:31.346 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:31.354 # of the computation is cached by cruncher
2025-07-01 03:04:31.366 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:31.382 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:31.390 cruncher.ratio() > best_ratio:
2025-07-01 03:04:31.402 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:31.414 if best_ratio < cutoff:
2025-07-01 03:04:31.422 # no non-identical "pretty close" pair
2025-07-01 03:04:31.434 if eqi is None:
2025-07-01 03:04:31.444 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:31.462 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:31.474 return
2025-07-01 03:04:31.482 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:31.487 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:31.492 else:
2025-07-01 03:04:31.497 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:31.501 eqi = None
2025-07-01 03:04:31.506
2025-07-01 03:04:31.511 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:31.516 # identical
2025-07-01 03:04:31.520
2025-07-01 03:04:31.525 # pump out diffs from before the synch point
2025-07-01 03:04:31.530 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:31.535
2025-07-01 03:04:31.539 # do intraline marking on the synch pair
2025-07-01 03:04:31.543 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:31.548 if eqi is None:
2025-07-01 03:04:31.552 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:31.557 atags = btags = ""
2025-07-01 03:04:31.562 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:31.567 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:31.573 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:31.578 if tag == 'replace':
2025-07-01 03:04:31.586 atags += '^' * la
2025-07-01 03:04:31.592 btags += '^' * lb
2025-07-01 03:04:31.597 elif tag == 'delete':
2025-07-01 03:04:31.601 atags += '-' * la
2025-07-01 03:04:31.606 elif tag == 'insert':
2025-07-01 03:04:31.611 btags += '+' * lb
2025-07-01 03:04:31.616 elif tag == 'equal':
2025-07-01 03:04:31.621 atags += ' ' * la
2025-07-01 03:04:31.625 btags += ' ' * lb
2025-07-01 03:04:31.634 else:
2025-07-01 03:04:31.646 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:31.656 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:31.663 else:
2025-07-01 03:04:31.668 # the synch pair is identical
2025-07-01 03:04:31.672 yield '  ' + aelt
2025-07-01 03:04:31.678
2025-07-01 03:04:31.682 # pump out diffs from after the synch point
2025-07-01 03:04:31.687 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:31.692
2025-07-01 03:04:31.696 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:31.701 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:31.706
2025-07-01 03:04:31.714 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:31.729 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:31.738 alo = 388, ahi = 1101
2025-07-01 03:04:31.753 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:31.769 blo = 388, bhi = 1101
2025-07-01 03:04:31.780
2025-07-01 03:04:31.790 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:31.808 g = []
2025-07-01 03:04:31.816 if alo < ahi:
2025-07-01 03:04:31.828 if blo < bhi:
2025-07-01 03:04:31.836 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:31.853 else:
2025-07-01 03:04:31.862 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:31.877 elif blo < bhi:
2025-07-01 03:04:31.891 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:31.901
2025-07-01 03:04:31.914 >       yield from g
2025-07-01 03:04:31.926
2025-07-01 03:04:31.942 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:31.954 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:31.966
2025-07-01 03:04:31.978 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:31.990 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:31.997 alo = 388, ahi = 1101
2025-07-01 03:04:32.018 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:32.034 blo = 388, bhi = 1101
2025-07-01 03:04:32.042
2025-07-01 03:04:32.053 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:32.074 r"""
2025-07-01 03:04:32.082 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:32.094 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:32.110 synch point, and intraline difference marking is done on the
2025-07-01 03:04:32.122 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:32.134
2025-07-01 03:04:32.146 Example:
2025-07-01 03:04:32.158
2025-07-01 03:04:32.166 >>> d = Differ()
2025-07-01 03:04:32.178 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:32.194 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:32.206 >>> print(''.join(results), end="")
2025-07-01 03:04:32.214 - abcDefghiJkl
2025-07-01 03:04:32.242 + abcdefGhijkl
2025-07-01 03:04:32.258 """
2025-07-01 03:04:32.270
2025-07-01 03:04:32.282 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:32.290 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:32.306 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:32.318 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:32.330 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:32.338
2025-07-01 03:04:32.350 # search for the pair that matches best without being identical
2025-07-01 03:04:32.362 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:32.369 # on junk -- unless we have to)
2025-07-01 03:04:32.382 for j in range(blo, bhi):
2025-07-01 03:04:32.389 bj = b[j]
2025-07-01 03:04:32.402 cruncher.set_seq2(bj)
2025-07-01 03:04:32.410 for i in range(alo, ahi):
2025-07-01 03:04:32.421 ai = a[i]
2025-07-01 03:04:32.430 if ai == bj:
2025-07-01 03:04:32.442 if eqi is None:
2025-07-01 03:04:32.450 eqi, eqj = i, j
2025-07-01 03:04:32.463 continue
2025-07-01 03:04:32.474 cruncher.set_seq1(ai)
2025-07-01 03:04:32.486 # computing similarity is expensive, so use the quick
2025-07-01 03:04:32.498 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:32.510 # compares by a factor of 3.
2025-07-01 03:04:32.521 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:32.534 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:32.544 # of the computation is cached by cruncher
2025-07-01 03:04:32.562 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:32.576 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:32.586 cruncher.ratio() > best_ratio:
2025-07-01 03:04:32.598 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:32.607 if best_ratio < cutoff:
2025-07-01 03:04:32.618 # no non-identical "pretty close" pair
2025-07-01 03:04:32.631 if eqi is None:
2025-07-01 03:04:32.643 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:32.657 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:32.666 return
2025-07-01 03:04:32.678 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:32.693 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:32.702 else:
2025-07-01 03:04:32.714 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:32.725 eqi = None
2025-07-01 03:04:32.734
2025-07-01 03:04:32.746 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:32.755 # identical
2025-07-01 03:04:32.773
2025-07-01 03:04:32.786 # pump out diffs from before the synch point
2025-07-01 03:04:32.798 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:32.814
2025-07-01 03:04:32.822 # do intraline marking on the synch pair
2025-07-01 03:04:32.831 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:32.846 if eqi is None:
2025-07-01 03:04:32.862 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:32.874 atags = btags = ""
2025-07-01 03:04:32.886 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:32.898 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:32.906 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:32.918 if tag == 'replace':
2025-07-01 03:04:32.925 atags += '^' * la
2025-07-01 03:04:32.941 btags += '^' * lb
2025-07-01 03:04:32.950 elif tag == 'delete':
2025-07-01 03:04:32.964 atags += '-' * la
2025-07-01 03:04:32.986 elif tag == 'insert':
2025-07-01 03:04:32.994 btags += '+' * lb
2025-07-01 03:04:33.008 elif tag == 'equal':
2025-07-01 03:04:33.020 atags += ' ' * la
2025-07-01 03:04:33.033 btags += ' ' * lb
2025-07-01 03:04:33.047 else:
2025-07-01 03:04:33.058 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:33.071 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:33.086 else:
2025-07-01 03:04:33.098 # the synch pair is identical
2025-07-01 03:04:33.110 yield '  ' + aelt
2025-07-01 03:04:33.126
2025-07-01 03:04:33.134 # pump out diffs from after the synch point
2025-07-01 03:04:33.147 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:33.158
2025-07-01 03:04:33.172 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:33.184 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:33.189
2025-07-01 03:04:33.198 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:33.204 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:33.209 alo = 389, ahi = 1101
2025-07-01 03:04:33.216 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:33.221 blo = 389, bhi = 1101
2025-07-01 03:04:33.226
2025-07-01 03:04:33.232 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:33.237 g = []
2025-07-01 03:04:33.242 if alo < ahi:
2025-07-01 03:04:33.247 if blo < bhi:
2025-07-01 03:04:33.253 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:33.259 else:
2025-07-01 03:04:33.265 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:33.270 elif blo < bhi:
2025-07-01 03:04:33.276 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:33.281
2025-07-01 03:04:33.288 >       yield from g
2025-07-01 03:04:33.295
2025-07-01 03:04:33.300 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:33.306 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:33.311
2025-07-01 03:04:33.317 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:33.324 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:33.328 alo = 389, ahi = 1101
2025-07-01 03:04:33.334 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:33.338 blo = 389, bhi = 1101
2025-07-01 03:04:33.343
2025-07-01 03:04:33.348 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:33.353 r"""
2025-07-01 03:04:33.358 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:33.365 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:33.371 synch point, and intraline difference marking is done on the
2025-07-01 03:04:33.378 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:33.384
2025-07-01 03:04:33.391 Example:
2025-07-01 03:04:33.399
2025-07-01 03:04:33.406 >>> d = Differ()
2025-07-01 03:04:33.415 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:33.426 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:33.437 >>> print(''.join(results), end="")
2025-07-01 03:04:33.443 - abcDefghiJkl
2025-07-01 03:04:33.467 + abcdefGhijkl
2025-07-01 03:04:33.502 """
2025-07-01 03:04:33.514
2025-07-01 03:04:33.527 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:33.549 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:33.565 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:33.573 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:33.586 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:33.606
2025-07-01 03:04:33.622 # search for the pair that matches best without being identical
2025-07-01 03:04:33.634 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:33.654 # on junk -- unless we have to)
2025-07-01 03:04:33.666 for j in range(blo, bhi):
2025-07-01 03:04:33.678 bj = b[j]
2025-07-01 03:04:33.694 cruncher.set_seq2(bj)
2025-07-01 03:04:33.711 for i in range(alo, ahi):
2025-07-01 03:04:33.727 ai = a[i]
2025-07-01 03:04:33.734 if ai == bj:
2025-07-01 03:04:33.750 if eqi is None:
2025-07-01 03:04:33.758 eqi, eqj = i, j
2025-07-01 03:04:33.774 continue
2025-07-01 03:04:33.794 cruncher.set_seq1(ai)
2025-07-01 03:04:33.818 # computing similarity is expensive, so use the quick
2025-07-01 03:04:33.835 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:33.851 # compares by a factor of 3.
2025-07-01 03:04:33.867 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:33.883 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:33.900 # of the computation is cached by cruncher
2025-07-01 03:04:33.919 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:33.935 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:33.949 cruncher.ratio() > best_ratio:
2025-07-01 03:04:33.967 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:33.974 if best_ratio < cutoff:
2025-07-01 03:04:33.986 # no non-identical "pretty close" pair
2025-07-01 03:04:33.998 if eqi is None:
2025-07-01 03:04:34.014 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:34.030 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:34.046 return
2025-07-01 03:04:34.062 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:34.087 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:34.102 else:
2025-07-01 03:04:34.118 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:34.134 eqi = None
2025-07-01 03:04:34.146
2025-07-01 03:04:34.154 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:34.169 # identical
2025-07-01 03:04:34.182
2025-07-01 03:04:34.195 # pump out diffs from before the synch point
2025-07-01 03:04:34.210 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:34.220
2025-07-01 03:04:34.230 # do intraline marking on the synch pair
2025-07-01 03:04:34.242 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:34.248 if eqi is None:
2025-07-01 03:04:34.253 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:34.258 atags = btags = ""
2025-07-01 03:04:34.263 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:34.270 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:34.275 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:34.280 if tag == 'replace':
2025-07-01 03:04:34.285 atags += '^' * la
2025-07-01 03:04:34.289 btags += '^' * lb
2025-07-01 03:04:34.294 elif tag == 'delete':
2025-07-01 03:04:34.299 atags += '-' * la
2025-07-01 03:04:34.303 elif tag == 'insert':
2025-07-01 03:04:34.308 btags += '+' * lb
2025-07-01 03:04:34.312 elif tag == 'equal':
2025-07-01 03:04:34.317 atags += ' ' * la
2025-07-01 03:04:34.322 btags += ' ' * lb
2025-07-01 03:04:34.326 else:
2025-07-01 03:04:34.333 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:34.342 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:34.350 else:
2025-07-01 03:04:34.361 # the synch pair is identical
2025-07-01 03:04:34.382 yield '  ' + aelt
2025-07-01 03:04:34.390
2025-07-01 03:04:34.402 # pump out diffs from after the synch point
2025-07-01 03:04:34.414 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:34.426
2025-07-01 03:04:34.434 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:34.450 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:34.458
2025-07-01 03:04:34.466 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:34.478 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:34.488 alo = 390, ahi = 1101
2025-07-01 03:04:34.506 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:34.518 blo = 390, bhi = 1101
2025-07-01 03:04:34.526
2025-07-01 03:04:34.542 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:34.550 g = []
2025-07-01 03:04:34.558 if alo < ahi:
2025-07-01 03:04:34.570 if blo < bhi:
2025-07-01 03:04:34.578 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:34.590 else:
2025-07-01 03:04:34.598 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:34.610 elif blo < bhi:
2025-07-01 03:04:34.618 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:34.630
2025-07-01 03:04:34.638 >       yield from g
2025-07-01 03:04:34.650
2025-07-01 03:04:34.658 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:34.670 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:34.678
2025-07-01 03:04:34.690 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:34.706 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:34.714 alo = 390, ahi = 1101
2025-07-01 03:04:34.730 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:34.746 blo = 390, bhi = 1101
2025-07-01 03:04:34.762
2025-07-01 03:04:34.778 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:34.790 r"""
2025-07-01 03:04:34.798 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:34.810 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:34.825 synch point, and intraline difference marking is done on the
2025-07-01 03:04:34.838 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:34.846
2025-07-01 03:04:34.862 Example:
2025-07-01 03:04:34.874
2025-07-01 03:04:34.882 >>> d = Differ()
2025-07-01 03:04:34.894 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:34.910 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:34.918 >>> print(''.join(results), end="")
2025-07-01 03:04:34.930 - abcDefghiJkl
2025-07-01 03:04:34.950 + abcdefGhijkl
2025-07-01 03:04:34.978 """
2025-07-01 03:04:34.986
2025-07-01 03:04:35.002 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:35.018 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:35.034 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:35.042 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:35.050 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:35.062
2025-07-01 03:04:35.070 # search for the pair that matches best without being identical
2025-07-01 03:04:35.082 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:35.090 # on junk -- unless we have to)
2025-07-01 03:04:35.102 for j in range(blo, bhi):
2025-07-01 03:04:35.111 bj = b[j]
2025-07-01 03:04:35.126 cruncher.set_seq2(bj)
2025-07-01 03:04:35.138 for i in range(alo, ahi):
2025-07-01 03:04:35.146 ai = a[i]
2025-07-01 03:04:35.158 if ai == bj:
2025-07-01 03:04:35.174 if eqi is None:
2025-07-01 03:04:35.186 eqi, eqj = i, j
2025-07-01 03:04:35.198 continue
2025-07-01 03:04:35.208 cruncher.set_seq1(ai)
2025-07-01 03:04:35.223 # computing similarity is expensive, so use the quick
2025-07-01 03:04:35.234 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:35.249 # compares by a factor of 3.
2025-07-01 03:04:35.261 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:35.268 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:35.282 # of the computation is cached by cruncher
2025-07-01 03:04:35.295 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:35.308 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:35.325 cruncher.ratio() > best_ratio:
2025-07-01 03:04:35.338 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:35.350 if best_ratio < cutoff:
2025-07-01 03:04:35.358 # no non-identical "pretty close" pair
2025-07-01 03:04:35.370 if eqi is None:
2025-07-01 03:04:35.386 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:35.394 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:35.405 return
2025-07-01 03:04:35.414 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:35.427 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:35.434 else:
2025-07-01 03:04:35.449 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:35.461 eqi = None
2025-07-01 03:04:35.470
2025-07-01 03:04:35.483 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:35.490 # identical
2025-07-01 03:04:35.504
2025-07-01 03:04:35.514 # pump out diffs from before the synch point
2025-07-01 03:04:35.526 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:35.539
2025-07-01 03:04:35.552 # do intraline marking on the synch pair
2025-07-01 03:04:35.573 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:35.589 if eqi is None:
2025-07-01 03:04:35.602 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:35.618 atags = btags = ""
2025-07-01 03:04:35.630 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:35.642 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:35.658 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:35.673 if tag == 'replace':
2025-07-01 03:04:35.686 atags += '^' * la
2025-07-01 03:04:35.703 btags += '^' * lb
2025-07-01 03:04:35.718 elif tag == 'delete':
2025-07-01 03:04:35.735 atags += '-' * la
2025-07-01 03:04:35.746 elif tag == 'insert':
2025-07-01 03:04:35.757 btags += '+' * lb
2025-07-01 03:04:35.766 elif tag == 'equal':
2025-07-01 03:04:35.778 atags += ' ' * la
2025-07-01 03:04:35.790 btags += ' ' * lb
2025-07-01 03:04:35.802 else:
2025-07-01 03:04:35.814 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:35.826 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:35.838 else:
2025-07-01 03:04:35.850 # the synch pair is identical
2025-07-01 03:04:35.862 yield '  ' + aelt
2025-07-01 03:04:35.878
2025-07-01 03:04:35.890 # pump out diffs from after the synch point
2025-07-01 03:04:35.906 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:35.916
2025-07-01 03:04:35.930 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:35.938 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:35.950
2025-07-01 03:04:35.958 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:35.970 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:35.989 alo = 391, ahi = 1101
2025-07-01 03:04:36.000 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:36.014 blo = 391, bhi = 1101
2025-07-01 03:04:36.024
2025-07-01 03:04:36.033 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:36.041 g = []
2025-07-01 03:04:36.047 if alo < ahi:
2025-07-01 03:04:36.053 if blo < bhi:
2025-07-01 03:04:36.059 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:36.064 else:
2025-07-01 03:04:36.071 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:36.080 elif blo < bhi:
2025-07-01 03:04:36.086 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:36.091
2025-07-01 03:04:36.097 >       yield from g
2025-07-01 03:04:36.102
2025-07-01 03:04:36.108 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:36.113 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:36.118
2025-07-01 03:04:36.122 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:36.127 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:36.132 alo = 391, ahi = 1101
2025-07-01 03:04:36.138 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:36.154 blo = 391, bhi = 1101
2025-07-01 03:04:36.162
2025-07-01 03:04:36.174 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:36.186 r"""
2025-07-01 03:04:36.195 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:36.202 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:36.214 synch point, and intraline difference marking is done on the
2025-07-01 03:04:36.225 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:36.238
2025-07-01 03:04:36.250 Example:
2025-07-01 03:04:36.262
2025-07-01 03:04:36.277 >>> d = Differ()
2025-07-01 03:04:36.295 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:36.311 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:36.325 >>> print(''.join(results), end="")
2025-07-01 03:04:36.335 - abcDefghiJkl
2025-07-01 03:04:36.366 + abcdefGhijkl
2025-07-01 03:04:36.382 """
2025-07-01 03:04:36.398
2025-07-01 03:04:36.410 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:36.418 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:36.431 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:36.442 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:36.454 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:36.470
2025-07-01 03:04:36.482 # search for the pair that matches best without being identical
2025-07-01 03:04:36.494 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:36.502 # on junk -- unless we have to)
2025-07-01 03:04:36.514 for j in range(blo, bhi):
2025-07-01 03:04:36.522 bj = b[j]
2025-07-01 03:04:36.534 cruncher.set_seq2(bj)
2025-07-01 03:04:36.542 for i in range(alo, ahi):
2025-07-01 03:04:36.554 ai = a[i]
2025-07-01 03:04:36.562 if ai == bj:
2025-07-01 03:04:36.574 if eqi is None:
2025-07-01 03:04:36.586 eqi, eqj = i, j
2025-07-01 03:04:36.598 continue
2025-07-01 03:04:36.606 cruncher.set_seq1(ai)
2025-07-01 03:04:36.622 # computing similarity is expensive, so use the quick
2025-07-01 03:04:36.634 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:36.650 # compares by a factor of 3.
2025-07-01 03:04:36.658 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:36.674 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:36.686 # of the computation is cached by cruncher
2025-07-01 03:04:36.698 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:36.714 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:36.726 cruncher.ratio() > best_ratio:
2025-07-01 03:04:36.734 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:36.746 if best_ratio < cutoff:
2025-07-01 03:04:36.757 # no non-identical "pretty close" pair
2025-07-01 03:04:36.770 if eqi is None:
2025-07-01 03:04:36.782 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:36.794 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:36.802 return
2025-07-01 03:04:36.814 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:36.826 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:36.841 else:
2025-07-01 03:04:36.849 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:36.865 eqi = None
2025-07-01 03:04:36.872
2025-07-01 03:04:36.889 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:36.905 # identical
2025-07-01 03:04:36.911
2025-07-01 03:04:36.929 # pump out diffs from before the synch point
2025-07-01 03:04:36.942 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:36.955
2025-07-01 03:04:36.962 # do intraline marking on the synch pair
2025-07-01 03:04:36.974 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:36.986 if eqi is None:
2025-07-01 03:04:36.994 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:37.002 atags = btags = ""
2025-07-01 03:04:37.018 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:37.026 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:37.034 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:37.046 if tag == 'replace':
2025-07-01 03:04:37.054 atags += '^' * la
2025-07-01 03:04:37.066 btags += '^' * lb
2025-07-01 03:04:37.074 elif tag == 'delete':
2025-07-01 03:04:37.086 atags += '-' * la
2025-07-01 03:04:37.097 elif tag == 'insert':
2025-07-01 03:04:37.110 btags += '+' * lb
2025-07-01 03:04:37.126 elif tag == 'equal':
2025-07-01 03:04:37.134 atags += ' ' * la
2025-07-01 03:04:37.142 btags += ' ' * lb
2025-07-01 03:04:37.152 else:
2025-07-01 03:04:37.164 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:37.177 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:37.190 else:
2025-07-01 03:04:37.202 # the synch pair is identical
2025-07-01 03:04:37.214 yield '  ' + aelt
2025-07-01 03:04:37.226
2025-07-01 03:04:37.237 # pump out diffs from after the synch point
2025-07-01 03:04:37.250 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:37.262
2025-07-01 03:04:37.270 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:37.282 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:37.298
2025-07-01 03:04:37.306 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:37.324 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:37.341 alo = 392, ahi = 1101
2025-07-01 03:04:37.362 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:37.373 blo = 392, bhi = 1101
2025-07-01 03:04:37.384
2025-07-01 03:04:37.391 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:37.396 g = []
2025-07-01 03:04:37.401 if alo < ahi:
2025-07-01 03:04:37.405 if blo < bhi:
2025-07-01 03:04:37.410 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:37.415 else:
2025-07-01 03:04:37.419 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:37.424 elif blo < bhi:
2025-07-01 03:04:37.429 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:37.433
2025-07-01 03:04:37.438 >       yield from g
2025-07-01 03:04:37.443
2025-07-01 03:04:37.447 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:37.452 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:37.456
2025-07-01 03:04:37.461 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:37.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:37.471 alo = 392, ahi = 1101
2025-07-01 03:04:37.477 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:37.486 blo = 392, bhi = 1101
2025-07-01 03:04:37.493
2025-07-01 03:04:37.498 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:37.502 r"""
2025-07-01 03:04:37.507 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:37.512 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:37.518 synch point, and intraline difference marking is done on the
2025-07-01 03:04:37.523 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:37.528
2025-07-01 03:04:37.533 Example:
2025-07-01 03:04:37.538
2025-07-01 03:04:37.543 >>> d = Differ()
2025-07-01 03:04:37.548 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:37.553 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:37.559 >>> print(''.join(results), end="")
2025-07-01 03:04:37.564 - abcDefghiJkl
2025-07-01 03:04:37.578 + abcdefGhijkl
2025-07-01 03:04:37.589 """
2025-07-01 03:04:37.594
2025-07-01 03:04:37.599 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:37.604 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:37.609 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:37.614 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:37.619 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:37.624
2025-07-01 03:04:37.630 # search for the pair that matches best without being identical
2025-07-01 03:04:37.635 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:37.640 # on junk -- unless we have to)
2025-07-01 03:04:37.644 for j in range(blo, bhi):
2025-07-01 03:04:37.649 bj = b[j]
2025-07-01 03:04:37.654 cruncher.set_seq2(bj)
2025-07-01 03:04:37.658 for i in range(alo, ahi):
2025-07-01 03:04:37.663 ai = a[i]
2025-07-01 03:04:37.668 if ai == bj:
2025-07-01 03:04:37.673 if eqi is None:
2025-07-01 03:04:37.677 eqi, eqj = i, j
2025-07-01 03:04:37.682 continue
2025-07-01 03:04:37.689 cruncher.set_seq1(ai)
2025-07-01 03:04:37.702 # computing similarity is expensive, so use the quick
2025-07-01 03:04:37.708 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:37.718 # compares by a factor of 3.
2025-07-01 03:04:37.723 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:37.732 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:37.746 # of the computation is cached by cruncher
2025-07-01 03:04:37.757 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:37.766 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:37.781 cruncher.ratio() > best_ratio:
2025-07-01 03:04:37.791 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:37.806 if best_ratio < cutoff:
2025-07-01 03:04:37.814 # no non-identical "pretty close" pair
2025-07-01 03:04:37.819 if eqi is None:
2025-07-01 03:04:37.828 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:37.833 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:37.838 return
2025-07-01 03:04:37.843 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:37.847 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:37.852 else:
2025-07-01 03:04:37.857 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:37.862 eqi = None
2025-07-01 03:04:37.867
2025-07-01 03:04:37.872 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:37.877 # identical
2025-07-01 03:04:37.881
2025-07-01 03:04:37.886 # pump out diffs from before the synch point
2025-07-01 03:04:37.891 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:37.895
2025-07-01 03:04:37.900 # do intraline marking on the synch pair
2025-07-01 03:04:37.905 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:37.909 if eqi is None:
2025-07-01 03:04:37.914 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:37.919 atags = btags = ""
2025-07-01 03:04:37.924 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:37.929 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:37.933 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:37.939 if tag == 'replace':
2025-07-01 03:04:37.945 atags += '^' * la
2025-07-01 03:04:37.950 btags += '^' * lb
2025-07-01 03:04:37.955 elif tag == 'delete':
2025-07-01 03:04:37.959 atags += '-' * la
2025-07-01 03:04:37.964 elif tag == 'insert':
2025-07-01 03:04:37.969 btags += '+' * lb
2025-07-01 03:04:37.974 elif tag == 'equal':
2025-07-01 03:04:37.978 atags += ' ' * la
2025-07-01 03:04:37.983 btags += ' ' * lb
2025-07-01 03:04:37.988 else:
2025-07-01 03:04:37.993 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:37.997 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:38.002 else:
2025-07-01 03:04:38.007 # the synch pair is identical
2025-07-01 03:04:38.012 yield '  ' + aelt
2025-07-01 03:04:38.017
2025-07-01 03:04:38.022 # pump out diffs from after the synch point
2025-07-01 03:04:38.026 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:38.031
2025-07-01 03:04:38.036 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:38.041 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:38.046
2025-07-01 03:04:38.051 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:38.057 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:38.062 alo = 393, ahi = 1101
2025-07-01 03:04:38.066 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:38.071 blo = 393, bhi = 1101
2025-07-01 03:04:38.076
2025-07-01 03:04:38.081 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:38.085 g = []
2025-07-01 03:04:38.090 if alo < ahi:
2025-07-01 03:04:38.095 if blo < bhi:
2025-07-01 03:04:38.099 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:38.104 else:
2025-07-01 03:04:38.109 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:38.114 elif blo < bhi:
2025-07-01 03:04:38.118 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:38.123
2025-07-01 03:04:38.128 >       yield from g
2025-07-01 03:04:38.132
2025-07-01 03:04:38.137 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:38.142 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:38.146
2025-07-01 03:04:38.151 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:38.156 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:38.160 alo = 393, ahi = 1101
2025-07-01 03:04:38.165 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:38.170 blo = 393, bhi = 1101
2025-07-01 03:04:38.175
2025-07-01 03:04:38.180 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:38.185 r"""
2025-07-01 03:04:38.189 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:38.195 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:38.200 synch point, and intraline difference marking is done on the
2025-07-01 03:04:38.204 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:38.209
2025-07-01 03:04:38.213 Example:
2025-07-01 03:04:38.218
2025-07-01 03:04:38.223 >>> d = Differ()
2025-07-01 03:04:38.227 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:38.232 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:38.237 >>> print(''.join(results), end="")
2025-07-01 03:04:38.242 - abcDefghiJkl
2025-07-01 03:04:38.252 + abcdefGhijkl
2025-07-01 03:04:38.261 """
2025-07-01 03:04:38.265
2025-07-01 03:04:38.270 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:38.275 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:38.280 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:38.285 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:38.289 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:38.294
2025-07-01 03:04:38.301 # search for the pair that matches best without being identical
2025-07-01 03:04:38.306 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:38.311 # on junk -- unless we have to)
2025-07-01 03:04:38.319 for j in range(blo, bhi):
2025-07-01 03:04:38.325 bj = b[j]
2025-07-01 03:04:38.330 cruncher.set_seq2(bj)
2025-07-01 03:04:38.334 for i in range(alo, ahi):
2025-07-01 03:04:38.339 ai = a[i]
2025-07-01 03:04:38.345 if ai == bj:
2025-07-01 03:04:38.351 if eqi is None:
2025-07-01 03:04:38.357 eqi, eqj = i, j
2025-07-01 03:04:38.363 continue
2025-07-01 03:04:38.368 cruncher.set_seq1(ai)
2025-07-01 03:04:38.374 # computing similarity is expensive, so use the quick
2025-07-01 03:04:38.381 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:38.386 # compares by a factor of 3.
2025-07-01 03:04:38.392 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:38.398 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:38.404 # of the computation is cached by cruncher
2025-07-01 03:04:38.410 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:38.415 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:38.420 cruncher.ratio() > best_ratio:
2025-07-01 03:04:38.425 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:38.429 if best_ratio < cutoff:
2025-07-01 03:04:38.434 # no non-identical "pretty close" pair
2025-07-01 03:04:38.438 if eqi is None:
2025-07-01 03:04:38.443 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:38.448 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:38.453 return
2025-07-01 03:04:38.458 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:38.464 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:38.469 else:
2025-07-01 03:04:38.474 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:38.479 eqi = None
2025-07-01 03:04:38.484
2025-07-01 03:04:38.489 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:38.494 # identical
2025-07-01 03:04:38.499
2025-07-01 03:04:38.504 # pump out diffs from before the synch point
2025-07-01 03:04:38.509 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:38.514
2025-07-01 03:04:38.520 # do intraline marking on the synch pair
2025-07-01 03:04:38.525 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:38.531 if eqi is None:
2025-07-01 03:04:38.537 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:38.542 atags = btags = ""
2025-07-01 03:04:38.547 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:38.552 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:38.558 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:38.565 if tag == 'replace':
2025-07-01 03:04:38.572 atags += '^' * la
2025-07-01 03:04:38.579 btags += '^' * lb
2025-07-01 03:04:38.586 elif tag == 'delete':
2025-07-01 03:04:38.594 atags += '-' * la
2025-07-01 03:04:38.601 elif tag == 'insert':
2025-07-01 03:04:38.608 btags += '+' * lb
2025-07-01 03:04:38.615 elif tag == 'equal':
2025-07-01 03:04:38.622 atags += ' ' * la
2025-07-01 03:04:38.628 btags += ' ' * lb
2025-07-01 03:04:38.633 else:
2025-07-01 03:04:38.640 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:38.646 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:38.652 else:
2025-07-01 03:04:38.657 # the synch pair is identical
2025-07-01 03:04:38.663 yield '  ' + aelt
2025-07-01 03:04:38.669
2025-07-01 03:04:38.675 # pump out diffs from after the synch point
2025-07-01 03:04:38.681 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:38.686
2025-07-01 03:04:38.692 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:38.701 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:38.707
2025-07-01 03:04:38.712 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:38.717 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:38.721 alo = 394, ahi = 1101
2025-07-01 03:04:38.728 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:38.732 blo = 394, bhi = 1101
2025-07-01 03:04:38.737
2025-07-01 03:04:38.741 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:38.745 g = []
2025-07-01 03:04:38.750 if alo < ahi:
2025-07-01 03:04:38.754 if blo < bhi:
2025-07-01 03:04:38.758 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:38.765 else:
2025-07-01 03:04:38.771 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:38.775 elif blo < bhi:
2025-07-01 03:04:38.780 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:38.784
2025-07-01 03:04:38.789 >       yield from g
2025-07-01 03:04:38.795
2025-07-01 03:04:38.801 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:38.810 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:38.819
2025-07-01 03:04:38.835 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:38.849 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:38.862 alo = 394, ahi = 1101
2025-07-01 03:04:38.878 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:38.898 blo = 394, bhi = 1101
2025-07-01 03:04:38.911
2025-07-01 03:04:38.923 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:38.936 r"""
2025-07-01 03:04:38.947 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:38.961 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:38.974 synch point, and intraline difference marking is done on the
2025-07-01 03:04:38.986 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:39.002
2025-07-01 03:04:39.017 Example:
2025-07-01 03:04:39.030
2025-07-01 03:04:39.050 >>> d = Differ()
2025-07-01 03:04:39.067 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:39.078 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:39.090 >>> print(''.join(results), end="")
2025-07-01 03:04:39.098 - abcDefghiJkl
2025-07-01 03:04:39.122 + abcdefGhijkl
2025-07-01 03:04:39.146 """
2025-07-01 03:04:39.152
2025-07-01 03:04:39.158 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:39.164 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:39.169 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:39.174 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:39.179 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:39.184
2025-07-01 03:04:39.188 # search for the pair that matches best without being identical
2025-07-01 03:04:39.193 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:39.197 # on junk -- unless we have to)
2025-07-01 03:04:39.202 for j in range(blo, bhi):
2025-07-01 03:04:39.206 bj = b[j]
2025-07-01 03:04:39.210 cruncher.set_seq2(bj)
2025-07-01 03:04:39.215 for i in range(alo, ahi):
2025-07-01 03:04:39.219 ai = a[i]
2025-07-01 03:04:39.224 if ai == bj:
2025-07-01 03:04:39.232 if eqi is None:
2025-07-01 03:04:39.237 eqi, eqj = i, j
2025-07-01 03:04:39.243 continue
2025-07-01 03:04:39.248 cruncher.set_seq1(ai)
2025-07-01 03:04:39.254 # computing similarity is expensive, so use the quick
2025-07-01 03:04:39.261 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:39.266 # compares by a factor of 3.
2025-07-01 03:04:39.271 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:39.277 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:39.283 # of the computation is cached by cruncher
2025-07-01 03:04:39.289 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:39.295 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:39.300 cruncher.ratio() > best_ratio:
2025-07-01 03:04:39.305 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:39.311 if best_ratio < cutoff:
2025-07-01 03:04:39.316 # no non-identical "pretty close" pair
2025-07-01 03:04:39.322 if eqi is None:
2025-07-01 03:04:39.328 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:39.333 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:39.339 return
2025-07-01 03:04:39.344 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:39.350 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:39.356 else:
2025-07-01 03:04:39.362 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:39.368 eqi = None
2025-07-01 03:04:39.373
2025-07-01 03:04:39.379 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:39.385 # identical
2025-07-01 03:04:39.390
2025-07-01 03:04:39.395 # pump out diffs from before the synch point
2025-07-01 03:04:39.401 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:39.406
2025-07-01 03:04:39.413 # do intraline marking on the synch pair
2025-07-01 03:04:39.418 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:39.423 if eqi is None:
2025-07-01 03:04:39.427 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:39.432 atags = btags = ""
2025-07-01 03:04:39.436 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:39.441 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:39.447 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:39.453 if tag == 'replace':
2025-07-01 03:04:39.458 atags += '^' * la
2025-07-01 03:04:39.463 btags += '^' * lb
2025-07-01 03:04:39.470 elif tag == 'delete':
2025-07-01 03:04:39.475 atags += '-' * la
2025-07-01 03:04:39.481 elif tag == 'insert':
2025-07-01 03:04:39.487 btags += '+' * lb
2025-07-01 03:04:39.492 elif tag == 'equal':
2025-07-01 03:04:39.496 atags += ' ' * la
2025-07-01 03:04:39.501 btags += ' ' * lb
2025-07-01 03:04:39.505 else:
2025-07-01 03:04:39.510 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:39.516 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:39.520 else:
2025-07-01 03:04:39.525 # the synch pair is identical
2025-07-01 03:04:39.529 yield '  ' + aelt
2025-07-01 03:04:39.534
2025-07-01 03:04:39.538 # pump out diffs from after the synch point
2025-07-01 03:04:39.543 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:39.547
2025-07-01 03:04:39.552 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:39.556 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:39.561
2025-07-01 03:04:39.566 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:39.571 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:39.576 alo = 395, ahi = 1101
2025-07-01 03:04:39.581 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:39.585 blo = 395, bhi = 1101
2025-07-01 03:04:39.590
2025-07-01 03:04:39.595 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:39.599 g = []
2025-07-01 03:04:39.603 if alo < ahi:
2025-07-01 03:04:39.608 if blo < bhi:
2025-07-01 03:04:39.612 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:39.617 else:
2025-07-01 03:04:39.621 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:39.626 elif blo < bhi:
2025-07-01 03:04:39.631 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:39.635
2025-07-01 03:04:39.640 >       yield from g
2025-07-01 03:04:39.644
2025-07-01 03:04:39.649 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:39.653 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:39.658
2025-07-01 03:04:39.663 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:39.667 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:39.672 alo = 395, ahi = 1101
2025-07-01 03:04:39.680 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:39.685 blo = 395, bhi = 1101
2025-07-01 03:04:39.690
2025-07-01 03:04:39.694 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:39.700 r"""
2025-07-01 03:04:39.704 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:39.709 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:39.713 synch point, and intraline difference marking is done on the
2025-07-01 03:04:39.718 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:39.722
2025-07-01 03:04:39.726 Example:
2025-07-01 03:04:39.730
2025-07-01 03:04:39.735 >>> d = Differ()
2025-07-01 03:04:39.739 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:39.744 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:39.748 >>> print(''.join(results), end="")
2025-07-01 03:04:39.753 - abcDefghiJkl
2025-07-01 03:04:39.761 + abcdefGhijkl
2025-07-01 03:04:39.770 """
2025-07-01 03:04:39.774
2025-07-01 03:04:39.779 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:39.783 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:39.788 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:39.793 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:39.797 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:39.802
2025-07-01 03:04:39.806 # search for the pair that matches best without being identical
2025-07-01 03:04:39.811 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:39.816 # on junk -- unless we have to)
2025-07-01 03:04:39.820 for j in range(blo, bhi):
2025-07-01 03:04:39.825 bj = b[j]
2025-07-01 03:04:39.831 cruncher.set_seq2(bj)
2025-07-01 03:04:39.835 for i in range(alo, ahi):
2025-07-01 03:04:39.840 ai = a[i]
2025-07-01 03:04:39.844 if ai == bj:
2025-07-01 03:04:39.849 if eqi is None:
2025-07-01 03:04:39.853 eqi, eqj = i, j
2025-07-01 03:04:39.861 continue
2025-07-01 03:04:39.866 cruncher.set_seq1(ai)
2025-07-01 03:04:39.871 # computing similarity is expensive, so use the quick
2025-07-01 03:04:39.876 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:39.880 # compares by a factor of 3.
2025-07-01 03:04:39.885 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:39.890 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:39.896 # of the computation is cached by cruncher
2025-07-01 03:04:39.901 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:39.905 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:39.910 cruncher.ratio() > best_ratio:
2025-07-01 03:04:39.917 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:39.922 if best_ratio < cutoff:
2025-07-01 03:04:39.927 # no non-identical "pretty close" pair
2025-07-01 03:04:39.934 if eqi is None:
2025-07-01 03:04:39.939 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:39.944 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:39.952 return
2025-07-01 03:04:39.957 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:39.962 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:39.969 else:
2025-07-01 03:04:39.978 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:39.991 eqi = None
2025-07-01 03:04:40.001
2025-07-01 03:04:40.020 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:40.034 # identical
2025-07-01 03:04:40.050
2025-07-01 03:04:40.062 # pump out diffs from before the synch point
2025-07-01 03:04:40.078 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:40.090
2025-07-01 03:04:40.102 # do intraline marking on the synch pair
2025-07-01 03:04:40.110 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:40.122 if eqi is None:
2025-07-01 03:04:40.129 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:40.142 atags = btags = ""
2025-07-01 03:04:40.154 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:40.161 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:40.174 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:40.192 if tag == 'replace':
2025-07-01 03:04:40.202 atags += '^' * la
2025-07-01 03:04:40.216 btags += '^' * lb
2025-07-01 03:04:40.230 elif tag == 'delete':
2025-07-01 03:04:40.242 atags += '-' * la
2025-07-01 03:04:40.254 elif tag == 'insert':
2025-07-01 03:04:40.266 btags += '+' * lb
2025-07-01 03:04:40.274 elif tag == 'equal':
2025-07-01 03:04:40.286 atags += ' ' * la
2025-07-01 03:04:40.294 btags += ' ' * lb
2025-07-01 03:04:40.306 else:
2025-07-01 03:04:40.318 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:40.325 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:40.342 else:
2025-07-01 03:04:40.350 # the synch pair is identical
2025-07-01 03:04:40.362 yield '  ' + aelt
2025-07-01 03:04:40.370
2025-07-01 03:04:40.385 # pump out diffs from after the synch point
2025-07-01 03:04:40.394 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:40.406
2025-07-01 03:04:40.414 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:40.426 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:40.434
2025-07-01 03:04:40.450 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:40.462 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:40.474 alo = 396, ahi = 1101
2025-07-01 03:04:40.486 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:40.494 blo = 396, bhi = 1101
2025-07-01 03:04:40.510
2025-07-01 03:04:40.518 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:40.530 g = []
2025-07-01 03:04:40.546 if alo < ahi:
2025-07-01 03:04:40.557 if blo < bhi:
2025-07-01 03:04:40.570 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:40.582 else:
2025-07-01 03:04:40.590 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:40.602 elif blo < bhi:
2025-07-01 03:04:40.610 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:40.622
2025-07-01 03:04:40.634 >       yield from g
2025-07-01 03:04:40.646
2025-07-01 03:04:40.658 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:40.666 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:40.678
2025-07-01 03:04:40.686 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:40.702 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:40.710 alo = 396, ahi = 1101
2025-07-01 03:04:40.726 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:40.738 blo = 396, bhi = 1101
2025-07-01 03:04:40.746
2025-07-01 03:04:40.762 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:40.770 r"""
2025-07-01 03:04:40.782 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:40.795 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:40.810 synch point, and intraline difference marking is done on the
2025-07-01 03:04:40.818 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:40.826
2025-07-01 03:04:40.838 Example:
2025-07-01 03:04:40.850
2025-07-01 03:04:40.866 >>> d = Differ()
2025-07-01 03:04:40.887 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:40.899 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:40.914 >>> print(''.join(results), end="")
2025-07-01 03:04:40.922 - abcDefghiJkl
2025-07-01 03:04:40.954 + abcdefGhijkl
2025-07-01 03:04:40.971 """
2025-07-01 03:04:40.988
2025-07-01 03:04:40.996 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:41.007 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:41.017 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:41.027 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:41.042 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:41.054
2025-07-01 03:04:41.062 # search for the pair that matches best without being identical
2025-07-01 03:04:41.070 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:41.083 # on junk -- unless we have to)
2025-07-01 03:04:41.095 for j in range(blo, bhi):
2025-07-01 03:04:41.102 bj = b[j]
2025-07-01 03:04:41.120 cruncher.set_seq2(bj)
2025-07-01 03:04:41.129 for i in range(alo, ahi):
2025-07-01 03:04:41.144 ai = a[i]
2025-07-01 03:04:41.158 if ai == bj:
2025-07-01 03:04:41.168 if eqi is None:
2025-07-01 03:04:41.186 eqi, eqj = i, j
2025-07-01 03:04:41.201 continue
2025-07-01 03:04:41.210 cruncher.set_seq1(ai)
2025-07-01 03:04:41.222 # computing similarity is expensive, so use the quick
2025-07-01 03:04:41.230 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:41.242 # compares by a factor of 3.
2025-07-01 03:04:41.254 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:41.270 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:41.280 # of the computation is cached by cruncher
2025-07-01 03:04:41.294 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:41.306 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:41.326 cruncher.ratio() > best_ratio:
2025-07-01 03:04:41.334 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:41.349 if best_ratio < cutoff:
2025-07-01 03:04:41.358 # no non-identical "pretty close" pair
2025-07-01 03:04:41.370 if eqi is None:
2025-07-01 03:04:41.382 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:41.393 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:41.406 return
2025-07-01 03:04:41.419 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:41.426 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:41.442 else:
2025-07-01 03:04:41.450 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:41.462 eqi = None
2025-07-01 03:04:41.470
2025-07-01 03:04:41.490 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:41.507 # identical
2025-07-01 03:04:41.526
2025-07-01 03:04:41.534 # pump out diffs from before the synch point
2025-07-01 03:04:41.546 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:41.556
2025-07-01 03:04:41.569 # do intraline marking on the synch pair
2025-07-01 03:04:41.580 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:41.594 if eqi is None:
2025-07-01 03:04:41.602 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:41.610 atags = btags = ""
2025-07-01 03:04:41.620 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:41.634 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:41.642 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:41.650 if tag == 'replace':
2025-07-01 03:04:41.666 atags += '^' * la
2025-07-01 03:04:41.674 btags += '^' * lb
2025-07-01 03:04:41.689 elif tag == 'delete':
2025-07-01 03:04:41.698 atags += '-' * la
2025-07-01 03:04:41.714 elif tag == 'insert':
2025-07-01 03:04:41.722 btags += '+' * lb
2025-07-01 03:04:41.730 elif tag == 'equal':
2025-07-01 03:04:41.742 atags += ' ' * la
2025-07-01 03:04:41.752 btags += ' ' * lb
2025-07-01 03:04:41.770 else:
2025-07-01 03:04:41.786 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:41.794 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:41.806 else:
2025-07-01 03:04:41.818 # the synch pair is identical
2025-07-01 03:04:41.830 yield '  ' + aelt
2025-07-01 03:04:41.850
2025-07-01 03:04:41.864 # pump out diffs from after the synch point
2025-07-01 03:04:41.878 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:41.890
2025-07-01 03:04:41.898 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:41.906 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:41.918
2025-07-01 03:04:41.926 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:41.938 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:41.950 alo = 397, ahi = 1101
2025-07-01 03:04:41.966 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:41.974 blo = 397, bhi = 1101
2025-07-01 03:04:41.986
2025-07-01 03:04:41.994 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:42.005 g = []
2025-07-01 03:04:42.018 if alo < ahi:
2025-07-01 03:04:42.034 if blo < bhi:
2025-07-01 03:04:42.046 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:42.058 else:
2025-07-01 03:04:42.076 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:42.090 elif blo < bhi:
2025-07-01 03:04:42.102 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:42.107
2025-07-01 03:04:42.111 >       yield from g
2025-07-01 03:04:42.116
2025-07-01 03:04:42.120 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:42.125 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:42.129
2025-07-01 03:04:42.134 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:42.139 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:42.143 alo = 397, ahi = 1101
2025-07-01 03:04:42.148 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:42.153 blo = 397, bhi = 1101
2025-07-01 03:04:42.157
2025-07-01 03:04:42.161 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:42.166 r"""
2025-07-01 03:04:42.172 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:42.177 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:42.182 synch point, and intraline difference marking is done on the
2025-07-01 03:04:42.187 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:42.191
2025-07-01 03:04:42.196 Example:
2025-07-01 03:04:42.200
2025-07-01 03:04:42.205 >>> d = Differ()
2025-07-01 03:04:42.209 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:42.215 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:42.220 >>> print(''.join(results), end="")
2025-07-01 03:04:42.224 - abcDefghiJkl
2025-07-01 03:04:42.233 + abcdefGhijkl
2025-07-01 03:04:42.242 """
2025-07-01 03:04:42.246
2025-07-01 03:04:42.251 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:42.255 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:42.259 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:42.264 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:42.269 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:42.274
2025-07-01 03:04:42.278 # search for the pair that matches best without being identical
2025-07-01 03:04:42.284 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:42.288 # on junk -- unless we have to)
2025-07-01 03:04:42.295 for j in range(blo, bhi):
2025-07-01 03:04:42.301 bj = b[j]
2025-07-01 03:04:42.305 cruncher.set_seq2(bj)
2025-07-01 03:04:42.310 for i in range(alo, ahi):
2025-07-01 03:04:42.314 ai = a[i]
2025-07-01 03:04:42.319 if ai == bj:
2025-07-01 03:04:42.323 if eqi is None:
2025-07-01 03:04:42.327 eqi, eqj = i, j
2025-07-01 03:04:42.332 continue
2025-07-01 03:04:42.336 cruncher.set_seq1(ai)
2025-07-01 03:04:42.341 # computing similarity is expensive, so use the quick
2025-07-01 03:04:42.345 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:42.350 # compares by a factor of 3.
2025-07-01 03:04:42.354 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:42.359 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:42.363 # of the computation is cached by cruncher
2025-07-01 03:04:42.368 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:42.373 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:42.377 cruncher.ratio() > best_ratio:
2025-07-01 03:04:42.382 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:42.387 if best_ratio < cutoff:
2025-07-01 03:04:42.392 # no non-identical "pretty close" pair
2025-07-01 03:04:42.396 if eqi is None:
2025-07-01 03:04:42.401 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:42.406 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:42.410 return
2025-07-01 03:04:42.414 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:42.419 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:42.423 else:
2025-07-01 03:04:42.428 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:42.434 eqi = None
2025-07-01 03:04:42.439
2025-07-01 03:04:42.445 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:42.450 # identical
2025-07-01 03:04:42.454
2025-07-01 03:04:42.459 # pump out diffs from before the synch point
2025-07-01 03:04:42.463 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:42.468
2025-07-01 03:04:42.472 # do intraline marking on the synch pair
2025-07-01 03:04:42.477 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:42.481 if eqi is None:
2025-07-01 03:04:42.485 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:42.490 atags = btags = ""
2025-07-01 03:04:42.494 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:42.499 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:42.503 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:42.508 if tag == 'replace':
2025-07-01 03:04:42.514 atags += '^' * la
2025-07-01 03:04:42.519 btags += '^' * lb
2025-07-01 03:04:42.523 elif tag == 'delete':
2025-07-01 03:04:42.528 atags += '-' * la
2025-07-01 03:04:42.532 elif tag == 'insert':
2025-07-01 03:04:42.537 btags += '+' * lb
2025-07-01 03:04:42.543 elif tag == 'equal':
2025-07-01 03:04:42.547 atags += ' ' * la
2025-07-01 03:04:42.552 btags += ' ' * lb
2025-07-01 03:04:42.556 else:
2025-07-01 03:04:42.564 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:42.570 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:42.574 else:
2025-07-01 03:04:42.578 # the synch pair is identical
2025-07-01 03:04:42.583 yield '  ' + aelt
2025-07-01 03:04:42.590
2025-07-01 03:04:42.598 # pump out diffs from after the synch point
2025-07-01 03:04:42.603 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:42.607
2025-07-01 03:04:42.612 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:42.616 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:42.621
2025-07-01 03:04:42.629 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:42.644 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:42.660 alo = 400, ahi = 1101
2025-07-01 03:04:42.669 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:42.681 blo = 400, bhi = 1101
2025-07-01 03:04:42.697
2025-07-01 03:04:42.710 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:42.718 g = []
2025-07-01 03:04:42.734 if alo < ahi:
2025-07-01 03:04:42.743 if blo < bhi:
2025-07-01 03:04:42.765 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:42.778 else:
2025-07-01 03:04:42.790 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:42.802 elif blo < bhi:
2025-07-01 03:04:42.822 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:42.834
2025-07-01 03:04:42.845 >       yield from g
2025-07-01 03:04:42.858
2025-07-01 03:04:42.866 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:42.882 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:42.898
2025-07-01 03:04:42.912 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:42.922 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:42.945 alo = 400, ahi = 1101
2025-07-01 03:04:42.958 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:42.966 blo = 400, bhi = 1101
2025-07-01 03:04:42.982
2025-07-01 03:04:42.996 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:43.009 r"""
2025-07-01 03:04:43.022 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:43.035 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:43.045 synch point, and intraline difference marking is done on the
2025-07-01 03:04:43.060 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:43.078
2025-07-01 03:04:43.093 Example:
2025-07-01 03:04:43.106
2025-07-01 03:04:43.117 >>> d = Differ()
2025-07-01 03:04:43.126 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:43.139 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:43.146 >>> print(''.join(results), end="")
2025-07-01 03:04:43.160 - abcDefghiJkl
2025-07-01 03:04:43.178 + abcdefGhijkl
2025-07-01 03:04:43.194 """
2025-07-01 03:04:43.208
2025-07-01 03:04:43.214 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:43.233 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:43.242 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:43.258 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:43.270 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:43.275
2025-07-01 03:04:43.294 # search for the pair that matches best without being identical
2025-07-01 03:04:43.300 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:43.305 # on junk -- unless we have to)
2025-07-01 03:04:43.310 for j in range(blo, bhi):
2025-07-01 03:04:43.314 bj = b[j]
2025-07-01 03:04:43.319 cruncher.set_seq2(bj)
2025-07-01 03:04:43.323 for i in range(alo, ahi):
2025-07-01 03:04:43.327 ai = a[i]
2025-07-01 03:04:43.332 if ai == bj:
2025-07-01 03:04:43.336 if eqi is None:
2025-07-01 03:04:43.341 eqi, eqj = i, j
2025-07-01 03:04:43.346 continue
2025-07-01 03:04:43.352 cruncher.set_seq1(ai)
2025-07-01 03:04:43.357 # computing similarity is expensive, so use the quick
2025-07-01 03:04:43.362 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:43.368 # compares by a factor of 3.
2025-07-01 03:04:43.374 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:43.379 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:43.384 # of the computation is cached by cruncher
2025-07-01 03:04:43.388 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:43.394 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:43.399 cruncher.ratio() > best_ratio:
2025-07-01 03:04:43.403 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:43.408 if best_ratio < cutoff:
2025-07-01 03:04:43.412 # no non-identical "pretty close" pair
2025-07-01 03:04:43.417 if eqi is None:
2025-07-01 03:04:43.422 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:43.426 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:43.431 return
2025-07-01 03:04:43.435 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:43.440 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:43.444 else:
2025-07-01 03:04:43.449 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:43.453 eqi = None
2025-07-01 03:04:43.458
2025-07-01 03:04:43.464 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:43.469 # identical
2025-07-01 03:04:43.473
2025-07-01 03:04:43.477 # pump out diffs from before the synch point
2025-07-01 03:04:43.482 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:43.486
2025-07-01 03:04:43.491 # do intraline marking on the synch pair
2025-07-01 03:04:43.496 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:43.500 if eqi is None:
2025-07-01 03:04:43.505 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:43.509 atags = btags = ""
2025-07-01 03:04:43.514 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:43.518 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:43.523 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:43.527 if tag == 'replace':
2025-07-01 03:04:43.532 atags += '^' * la
2025-07-01 03:04:43.536 btags += '^' * lb
2025-07-01 03:04:43.541 elif tag == 'delete':
2025-07-01 03:04:43.546 atags += '-' * la
2025-07-01 03:04:43.550 elif tag == 'insert':
2025-07-01 03:04:43.554 btags += '+' * lb
2025-07-01 03:04:43.559 elif tag == 'equal':
2025-07-01 03:04:43.564 atags += ' ' * la
2025-07-01 03:04:43.569 btags += ' ' * lb
2025-07-01 03:04:43.576 else:
2025-07-01 03:04:43.580 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:43.584 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:43.589 else:
2025-07-01 03:04:43.593 # the synch pair is identical
2025-07-01 03:04:43.598 yield '  ' + aelt
2025-07-01 03:04:43.604
2025-07-01 03:04:43.610 # pump out diffs from after the synch point
2025-07-01 03:04:43.626 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:43.635
2025-07-01 03:04:43.642 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:43.660 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:43.670
2025-07-01 03:04:43.680 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:43.694 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:43.703 alo = 401, ahi = 1101
2025-07-01 03:04:43.719 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:43.733 blo = 401, bhi = 1101
2025-07-01 03:04:43.748
2025-07-01 03:04:43.762 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:43.770 g = []
2025-07-01 03:04:43.777 if alo < ahi:
2025-07-01 03:04:43.794 if blo < bhi:
2025-07-01 03:04:43.802 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:43.817 else:
2025-07-01 03:04:43.826 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:43.842 elif blo < bhi:
2025-07-01 03:04:43.850 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:43.862
2025-07-01 03:04:43.870 >       yield from g
2025-07-01 03:04:43.877
2025-07-01 03:04:43.888 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:43.900 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:43.910
2025-07-01 03:04:43.922 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:43.930 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:43.946 alo = 401, ahi = 1101
2025-07-01 03:04:43.954 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:43.970 blo = 401, bhi = 1101
2025-07-01 03:04:43.978
2025-07-01 03:04:43.992 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:44.006 r"""
2025-07-01 03:04:44.014 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:44.026 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:44.034 synch point, and intraline difference marking is done on the
2025-07-01 03:04:44.046 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:44.058
2025-07-01 03:04:44.070 Example:
2025-07-01 03:04:44.086
2025-07-01 03:04:44.098 >>> d = Differ()
2025-07-01 03:04:44.114 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:44.130 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:44.138 >>> print(''.join(results), end="")
2025-07-01 03:04:44.146 - abcDefghiJkl
2025-07-01 03:04:44.168 + abcdefGhijkl
2025-07-01 03:04:44.194 """
2025-07-01 03:04:44.206
2025-07-01 03:04:44.221 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:44.230 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:44.242 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:44.254 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:44.262 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:44.274
2025-07-01 03:04:44.282 # search for the pair that matches best without being identical
2025-07-01 03:04:44.290 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:44.300 # on junk -- unless we have to)
2025-07-01 03:04:44.314 for j in range(blo, bhi):
2025-07-01 03:04:44.326 bj = b[j]
2025-07-01 03:04:44.343 cruncher.set_seq2(bj)
2025-07-01 03:04:44.359 for i in range(alo, ahi):
2025-07-01 03:04:44.374 ai = a[i]
2025-07-01 03:04:44.382 if ai == bj:
2025-07-01 03:04:44.398 if eqi is None:
2025-07-01 03:04:44.406 eqi, eqj = i, j
2025-07-01 03:04:44.422 continue
2025-07-01 03:04:44.430 cruncher.set_seq1(ai)
2025-07-01 03:04:44.448 # computing similarity is expensive, so use the quick
2025-07-01 03:04:44.458 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:44.474 # compares by a factor of 3.
2025-07-01 03:04:44.482 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:44.498 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:44.509 # of the computation is cached by cruncher
2025-07-01 03:04:44.518 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:44.531 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:44.542 cruncher.ratio() > best_ratio:
2025-07-01 03:04:44.549 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:44.558 if best_ratio < cutoff:
2025-07-01 03:04:44.572 # no non-identical "pretty close" pair
2025-07-01 03:04:44.590 if eqi is None:
2025-07-01 03:04:44.603 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:44.613 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:44.630 return
2025-07-01 03:04:44.637 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:44.650 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:44.661 else:
2025-07-01 03:04:44.673 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:44.686 eqi = None
2025-07-01 03:04:44.697
2025-07-01 03:04:44.714 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:44.726 # identical
2025-07-01 03:04:44.740
2025-07-01 03:04:44.754 # pump out diffs from before the synch point
2025-07-01 03:04:44.763 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:44.772
2025-07-01 03:04:44.785 # do intraline marking on the synch pair
2025-07-01 03:04:44.794 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:44.805 if eqi is None:
2025-07-01 03:04:44.814 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:44.829 atags = btags = ""
2025-07-01 03:04:44.838 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:44.848 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:44.861 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:44.874 if tag == 'replace':
2025-07-01 03:04:44.886 atags += '^' * la
2025-07-01 03:04:44.898 btags += '^' * lb
2025-07-01 03:04:44.904 elif tag == 'delete':
2025-07-01 03:04:44.911 atags += '-' * la
2025-07-01 03:04:44.917 elif tag == 'insert':
2025-07-01 03:04:44.922 btags += '+' * lb
2025-07-01 03:04:44.927 elif tag == 'equal':
2025-07-01 03:04:44.933 atags += ' ' * la
2025-07-01 03:04:44.938 btags += ' ' * lb
2025-07-01 03:04:44.943 else:
2025-07-01 03:04:44.949 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:44.955 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:44.966 else:
2025-07-01 03:04:44.982 # the synch pair is identical
2025-07-01 03:04:44.996 yield '  ' + aelt
2025-07-01 03:04:45.005
2025-07-01 03:04:45.018 # pump out diffs from after the synch point
2025-07-01 03:04:45.027 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:45.038
2025-07-01 03:04:45.049 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:45.059 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:45.067
2025-07-01 03:04:45.074 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:45.086 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:45.098 alo = 402, ahi = 1101
2025-07-01 03:04:45.118 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:45.125 blo = 402, bhi = 1101
2025-07-01 03:04:45.141
2025-07-01 03:04:45.150 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:45.162 g = []
2025-07-01 03:04:45.170 if alo < ahi:
2025-07-01 03:04:45.181 if blo < bhi:
2025-07-01 03:04:45.190 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:45.200 else:
2025-07-01 03:04:45.214 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:45.223 elif blo < bhi:
2025-07-01 03:04:45.233 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:45.239
2025-07-01 03:04:45.252 >       yield from g
2025-07-01 03:04:45.266
2025-07-01 03:04:45.278 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:45.290 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:45.298
2025-07-01 03:04:45.310 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:45.322 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:45.330 alo = 402, ahi = 1101
2025-07-01 03:04:45.342 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:45.354 blo = 402, bhi = 1101
2025-07-01 03:04:45.362
2025-07-01 03:04:45.374 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:45.382 r"""
2025-07-01 03:04:45.394 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:45.402 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:45.414 synch point, and intraline difference marking is done on the
2025-07-01 03:04:45.426 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:45.434
2025-07-01 03:04:45.440 Example:
2025-07-01 03:04:45.444
2025-07-01 03:04:45.449 >>> d = Differ()
2025-07-01 03:04:45.453 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:45.458 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:45.462 >>> print(''.join(results), end="")
2025-07-01 03:04:45.467 - abcDefghiJkl
2025-07-01 03:04:45.476 + abcdefGhijkl
2025-07-01 03:04:45.484 """
2025-07-01 03:04:45.489
2025-07-01 03:04:45.494 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:45.498 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:45.503 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:45.508 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:45.512 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:45.517
2025-07-01 03:04:45.522 # search for the pair that matches best without being identical
2025-07-01 03:04:45.526 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:45.531 # on junk -- unless we have to)
2025-07-01 03:04:45.536 for j in range(blo, bhi):
2025-07-01 03:04:45.540 bj = b[j]
2025-07-01 03:04:45.545 cruncher.set_seq2(bj)
2025-07-01 03:04:45.549 for i in range(alo, ahi):
2025-07-01 03:04:45.554 ai = a[i]
2025-07-01 03:04:45.558 if ai == bj:
2025-07-01 03:04:45.563 if eqi is None:
2025-07-01 03:04:45.568 eqi, eqj = i, j
2025-07-01 03:04:45.573 continue
2025-07-01 03:04:45.577 cruncher.set_seq1(ai)
2025-07-01 03:04:45.582 # computing similarity is expensive, so use the quick
2025-07-01 03:04:45.587 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:45.591 # compares by a factor of 3.
2025-07-01 03:04:45.596 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:45.601 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:45.605 # of the computation is cached by cruncher
2025-07-01 03:04:45.610 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:45.614 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:45.619 cruncher.ratio() > best_ratio:
2025-07-01 03:04:45.624 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:45.628 if best_ratio < cutoff:
2025-07-01 03:04:45.633 # no non-identical "pretty close" pair
2025-07-01 03:04:45.637 if eqi is None:
2025-07-01 03:04:45.642 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:45.646 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:45.651 return
2025-07-01 03:04:45.655 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:45.659 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:45.664 else:
2025-07-01 03:04:45.668 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:45.673 eqi = None
2025-07-01 03:04:45.677
2025-07-01 03:04:45.683 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:45.688 # identical
2025-07-01 03:04:45.693
2025-07-01 03:04:45.697 # pump out diffs from before the synch point
2025-07-01 03:04:45.702 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:45.707
2025-07-01 03:04:45.711 # do intraline marking on the synch pair
2025-07-01 03:04:45.716 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:45.720 if eqi is None:
2025-07-01 03:04:45.725 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:45.730 atags = btags = ""
2025-07-01 03:04:45.734 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:45.739 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:45.743 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:45.748 if tag == 'replace':
2025-07-01 03:04:45.753 atags += '^' * la
2025-07-01 03:04:45.757 btags += '^' * lb
2025-07-01 03:04:45.762 elif tag == 'delete':
2025-07-01 03:04:45.766 atags += '-' * la
2025-07-01 03:04:45.770 elif tag == 'insert':
2025-07-01 03:04:45.775 btags += '+' * lb
2025-07-01 03:04:45.779 elif tag == 'equal':
2025-07-01 03:04:45.783 atags += ' ' * la
2025-07-01 03:04:45.788 btags += ' ' * lb
2025-07-01 03:04:45.793 else:
2025-07-01 03:04:45.798 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:45.803 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:45.808 else:
2025-07-01 03:04:45.812 # the synch pair is identical
2025-07-01 03:04:45.817 yield '  ' + aelt
2025-07-01 03:04:45.821
2025-07-01 03:04:45.831 # pump out diffs from after the synch point
2025-07-01 03:04:45.835 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:45.841
2025-07-01 03:04:45.845 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:45.850 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:45.855
2025-07-01 03:04:45.859 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:45.864 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:45.869 alo = 403, ahi = 1101
2025-07-01 03:04:45.874 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:45.878 blo = 403, bhi = 1101
2025-07-01 03:04:45.882
2025-07-01 03:04:45.887 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:45.891 g = []
2025-07-01 03:04:45.896 if alo < ahi:
2025-07-01 03:04:45.901 if blo < bhi:
2025-07-01 03:04:45.906 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:45.911 else:
2025-07-01 03:04:45.915 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:45.920 elif blo < bhi:
2025-07-01 03:04:45.925 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:45.929
2025-07-01 03:04:45.934 >       yield from g
2025-07-01 03:04:45.938
2025-07-01 03:04:45.943 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:45.947 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:45.952
2025-07-01 03:04:45.956 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:45.961 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:45.966 alo = 403, ahi = 1101
2025-07-01 03:04:45.970 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:45.975 blo = 403, bhi = 1101
2025-07-01 03:04:45.979
2025-07-01 03:04:45.984 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:45.989 r"""
2025-07-01 03:04:45.993 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:45.998 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:46.002 synch point, and intraline difference marking is done on the
2025-07-01 03:04:46.007 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:46.012
2025-07-01 03:04:46.016 Example:
2025-07-01 03:04:46.021
2025-07-01 03:04:46.026 >>> d = Differ()
2025-07-01 03:04:46.030 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:46.035 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:46.040 >>> print(''.join(results), end="")
2025-07-01 03:04:46.044 - abcDefghiJkl
2025-07-01 03:04:46.053 + abcdefGhijkl
2025-07-01 03:04:46.062 """
2025-07-01 03:04:46.066
2025-07-01 03:04:46.071 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:46.075 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:46.080 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:46.084 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:46.091 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:46.096
2025-07-01 03:04:46.101 # search for the pair that matches best without being identical
2025-07-01 03:04:46.105 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:46.110 # on junk -- unless we have to)
2025-07-01 03:04:46.115 for j in range(blo, bhi):
2025-07-01 03:04:46.119 bj = b[j]
2025-07-01 03:04:46.124 cruncher.set_seq2(bj)
2025-07-01 03:04:46.128 for i in range(alo, ahi):
2025-07-01 03:04:46.133 ai = a[i]
2025-07-01 03:04:46.137 if ai == bj:
2025-07-01 03:04:46.141 if eqi is None:
2025-07-01 03:04:46.146 eqi, eqj = i, j
2025-07-01 03:04:46.151 continue
2025-07-01 03:04:46.155 cruncher.set_seq1(ai)
2025-07-01 03:04:46.160 # computing similarity is expensive, so use the quick
2025-07-01 03:04:46.164 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:46.169 # compares by a factor of 3.
2025-07-01 03:04:46.174 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:46.178 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:46.183 # of the computation is cached by cruncher
2025-07-01 03:04:46.188 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:46.192 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:46.197 cruncher.ratio() > best_ratio:
2025-07-01 03:04:46.201 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:46.206 if best_ratio < cutoff:
2025-07-01 03:04:46.210 # no non-identical "pretty close" pair
2025-07-01 03:04:46.215 if eqi is None:
2025-07-01 03:04:46.220 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:46.224 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:46.229 return
2025-07-01 03:04:46.233 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:46.238 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:46.243 else:
2025-07-01 03:04:46.250 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:46.254 eqi = None
2025-07-01 03:04:46.259
2025-07-01 03:04:46.263 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:46.268 # identical
2025-07-01 03:04:46.272
2025-07-01 03:04:46.277 # pump out diffs from before the synch point
2025-07-01 03:04:46.281 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:46.286
2025-07-01 03:04:46.290 # do intraline marking on the synch pair
2025-07-01 03:04:46.294 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:46.299 if eqi is None:
2025-07-01 03:04:46.304 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:46.309 atags = btags = ""
2025-07-01 03:04:46.313 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:46.318 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:46.323 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:46.327 if tag == 'replace':
2025-07-01 03:04:46.332 atags += '^' * la
2025-07-01 03:04:46.337 btags += '^' * lb
2025-07-01 03:04:46.342 elif tag == 'delete':
2025-07-01 03:04:46.346 atags += '-' * la
2025-07-01 03:04:46.351 elif tag == 'insert':
2025-07-01 03:04:46.355 btags += '+' * lb
2025-07-01 03:04:46.360 elif tag == 'equal':
2025-07-01 03:04:46.365 atags += ' ' * la
2025-07-01 03:04:46.370 btags += ' ' * lb
2025-07-01 03:04:46.374 else:
2025-07-01 03:04:46.379 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:46.384 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:46.391 else:
2025-07-01 03:04:46.397 # the synch pair is identical
2025-07-01 03:04:46.402 yield '  ' + aelt
2025-07-01 03:04:46.407
2025-07-01 03:04:46.411 # pump out diffs from after the synch point
2025-07-01 03:04:46.416 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:46.420
2025-07-01 03:04:46.425 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:46.429 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:46.434
2025-07-01 03:04:46.439 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:46.444 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:46.449 alo = 404, ahi = 1101
2025-07-01 03:04:46.455 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:46.459 blo = 404, bhi = 1101
2025-07-01 03:04:46.464
2025-07-01 03:04:46.470 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:46.474 g = []
2025-07-01 03:04:46.479 if alo < ahi:
2025-07-01 03:04:46.484 if blo < bhi:
2025-07-01 03:04:46.489 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:46.493 else:
2025-07-01 03:04:46.498 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:46.502 elif blo < bhi:
2025-07-01 03:04:46.507 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:46.511
2025-07-01 03:04:46.516 >       yield from g
2025-07-01 03:04:46.521
2025-07-01 03:04:46.525 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:46.530 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:46.535
2025-07-01 03:04:46.542 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:46.549 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:46.554 alo = 404, ahi = 1101
2025-07-01 03:04:46.559 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:46.564 blo = 404, bhi = 1101
2025-07-01 03:04:46.569
2025-07-01 03:04:46.573 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:46.581 r"""
2025-07-01 03:04:46.587 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:46.596 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:46.601 synch point, and intraline difference marking is done on the
2025-07-01 03:04:46.606 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:46.614
2025-07-01 03:04:46.618 Example:
2025-07-01 03:04:46.623
2025-07-01 03:04:46.627 >>> d = Differ()
2025-07-01 03:04:46.632 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:46.642 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:46.650 >>> print(''.join(results), end="")
2025-07-01 03:04:46.673 - abcDefghiJkl
2025-07-01 03:04:46.692 + abcdefGhijkl
2025-07-01 03:04:46.711 """
2025-07-01 03:04:46.721
2025-07-01 03:04:46.734 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:46.742 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:46.754 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:46.762 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:46.774 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:46.782
2025-07-01 03:04:46.794 # search for the pair that matches best without being identical
2025-07-01 03:04:46.806 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:46.814 # on junk -- unless we have to)
2025-07-01 03:04:46.832 for j in range(blo, bhi):
2025-07-01 03:04:46.842 bj = b[j]
2025-07-01 03:04:46.854 cruncher.set_seq2(bj)
2025-07-01 03:04:46.864 for i in range(alo, ahi):
2025-07-01 03:04:46.878 ai = a[i]
2025-07-01 03:04:46.895 if ai == bj:
2025-07-01 03:04:46.902 if eqi is None:
2025-07-01 03:04:46.913 eqi, eqj = i, j
2025-07-01 03:04:46.922 continue
2025-07-01 03:04:46.942 cruncher.set_seq1(ai)
2025-07-01 03:04:46.950 # computing similarity is expensive, so use the quick
2025-07-01 03:04:46.966 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:46.974 # compares by a factor of 3.
2025-07-01 03:04:46.986 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:46.994 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:47.010 # of the computation is cached by cruncher
2025-07-01 03:04:47.022 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:47.030 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:47.046 cruncher.ratio() > best_ratio:
2025-07-01 03:04:47.054 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:47.066 if best_ratio < cutoff:
2025-07-01 03:04:47.074 # no non-identical "pretty close" pair
2025-07-01 03:04:47.086 if eqi is None:
2025-07-01 03:04:47.094 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:47.106 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:47.118 return
2025-07-01 03:04:47.133 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:47.146 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:47.158 else:
2025-07-01 03:04:47.170 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:47.178 eqi = None
2025-07-01 03:04:47.190
2025-07-01 03:04:47.203 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:47.222 # identical
2025-07-01 03:04:47.232
2025-07-01 03:04:47.242 # pump out diffs from before the synch point
2025-07-01 03:04:47.258 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:47.266
2025-07-01 03:04:47.278 # do intraline marking on the synch pair
2025-07-01 03:04:47.286 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:47.296 if eqi is None:
2025-07-01 03:04:47.308 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:47.322 atags = btags = ""
2025-07-01 03:04:47.330 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:47.340 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:47.358 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:47.364 if tag == 'replace':
2025-07-01 03:04:47.383 atags += '^' * la
2025-07-01 03:04:47.398 btags += '^' * lb
2025-07-01 03:04:47.410 elif tag == 'delete':
2025-07-01 03:04:47.424 atags += '-' * la
2025-07-01 03:04:47.434 elif tag == 'insert':
2025-07-01 03:04:47.446 btags += '+' * lb
2025-07-01 03:04:47.458 elif tag == 'equal':
2025-07-01 03:04:47.474 atags += ' ' * la
2025-07-01 03:04:47.486 btags += ' ' * lb
2025-07-01 03:04:47.498 else:
2025-07-01 03:04:47.510 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:47.518 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:47.530 else:
2025-07-01 03:04:47.544 # the synch pair is identical
2025-07-01 03:04:47.554 yield '  ' + aelt
2025-07-01 03:04:47.570
2025-07-01 03:04:47.580 # pump out diffs from after the synch point
2025-07-01 03:04:47.594 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:47.605
2025-07-01 03:04:47.615 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:47.634 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:47.645
2025-07-01 03:04:47.659 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:47.670 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:47.678 alo = 405, ahi = 1101
2025-07-01 03:04:47.686 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:47.698 blo = 405, bhi = 1101
2025-07-01 03:04:47.712
2025-07-01 03:04:47.726 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:47.737 g = []
2025-07-01 03:04:47.755 if alo < ahi:
2025-07-01 03:04:47.771 if blo < bhi:
2025-07-01 03:04:47.777 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:47.786 else:
2025-07-01 03:04:47.798 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:47.814 elif blo < bhi:
2025-07-01 03:04:47.821 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:47.830
2025-07-01 03:04:47.842 >       yield from g
2025-07-01 03:04:47.850
2025-07-01 03:04:47.860 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:47.864 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:47.869
2025-07-01 03:04:47.873 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:47.878 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:47.883 alo = 405, ahi = 1101
2025-07-01 03:04:47.888 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:47.892 blo = 405, bhi = 1101
2025-07-01 03:04:47.896
2025-07-01 03:04:47.901 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:47.905 r"""
2025-07-01 03:04:47.910 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:47.915 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:47.919 synch point, and intraline difference marking is done on the
2025-07-01 03:04:47.924 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:47.928
2025-07-01 03:04:47.932 Example:
2025-07-01 03:04:47.937
2025-07-01 03:04:47.941 >>> d = Differ()
2025-07-01 03:04:47.945 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:47.950 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:47.954 >>> print(''.join(results), end="")
2025-07-01 03:04:47.959 - abcDefghiJkl
2025-07-01 03:04:47.968 + abcdefGhijkl
2025-07-01 03:04:47.979 """
2025-07-01 03:04:47.983
2025-07-01 03:04:47.988 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:47.993 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:47.997 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:48.001 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:48.007 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:48.011
2025-07-01 03:04:48.015 # search for the pair that matches best without being identical
2025-07-01 03:04:48.020 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:48.024 # on junk -- unless we have to)
2025-07-01 03:04:48.029 for j in range(blo, bhi):
2025-07-01 03:04:48.033 bj = b[j]
2025-07-01 03:04:48.037 cruncher.set_seq2(bj)
2025-07-01 03:04:48.042 for i in range(alo, ahi):
2025-07-01 03:04:48.046 ai = a[i]
2025-07-01 03:04:48.050 if ai == bj:
2025-07-01 03:04:48.055 if eqi is None:
2025-07-01 03:04:48.059 eqi, eqj = i, j
2025-07-01 03:04:48.063 continue
2025-07-01 03:04:48.068 cruncher.set_seq1(ai)
2025-07-01 03:04:48.072 # computing similarity is expensive, so use the quick
2025-07-01 03:04:48.076 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:48.081 # compares by a factor of 3.
2025-07-01 03:04:48.085 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:48.090 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:48.094 # of the computation is cached by cruncher
2025-07-01 03:04:48.099 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:48.103 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:48.107 cruncher.ratio() > best_ratio:
2025-07-01 03:04:48.112 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:48.117 if best_ratio < cutoff:
2025-07-01 03:04:48.121 # no non-identical "pretty close" pair
2025-07-01 03:04:48.126 if eqi is None:
2025-07-01 03:04:48.130 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:48.135 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:48.139 return
2025-07-01 03:04:48.144 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:48.149 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:48.153 else:
2025-07-01 03:04:48.158 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:48.164 eqi = None
2025-07-01 03:04:48.168
2025-07-01 03:04:48.173 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:48.177 # identical
2025-07-01 03:04:48.182
2025-07-01 03:04:48.187 # pump out diffs from before the synch point
2025-07-01 03:04:48.191 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:48.196
2025-07-01 03:04:48.200 # do intraline marking on the synch pair
2025-07-01 03:04:48.205 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:48.210 if eqi is None:
2025-07-01 03:04:48.214 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:48.219 atags = btags = ""
2025-07-01 03:04:48.223 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:48.229 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:48.236 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:48.241 if tag == 'replace':
2025-07-01 03:04:48.245 atags += '^' * la
2025-07-01 03:04:48.249 btags += '^' * lb
2025-07-01 03:04:48.254 elif tag == 'delete':
2025-07-01 03:04:48.258 atags += '-' * la
2025-07-01 03:04:48.263 elif tag == 'insert':
2025-07-01 03:04:48.267 btags += '+' * lb
2025-07-01 03:04:48.271 elif tag == 'equal':
2025-07-01 03:04:48.275 atags += ' ' * la
2025-07-01 03:04:48.280 btags += ' ' * lb
2025-07-01 03:04:48.284 else:
2025-07-01 03:04:48.288 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:48.293 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:48.297 else:
2025-07-01 03:04:48.301 # the synch pair is identical
2025-07-01 03:04:48.306 yield '  ' + aelt
2025-07-01 03:04:48.310
2025-07-01 03:04:48.315 # pump out diffs from after the synch point
2025-07-01 03:04:48.321 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:48.325
2025-07-01 03:04:48.329 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:48.334 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:48.338
2025-07-01 03:04:48.342 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:48.347 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:48.351 alo = 406, ahi = 1101
2025-07-01 03:04:48.356 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:48.361 blo = 406, bhi = 1101
2025-07-01 03:04:48.365
2025-07-01 03:04:48.369 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:48.375 g = []
2025-07-01 03:04:48.386 if alo < ahi:
2025-07-01 03:04:48.402 if blo < bhi:
2025-07-01 03:04:48.413 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:48.426 else:
2025-07-01 03:04:48.438 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:48.450 elif blo < bhi:
2025-07-01 03:04:48.462 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:48.474
2025-07-01 03:04:48.486 >       yield from g
2025-07-01 03:04:48.498
2025-07-01 03:04:48.510 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:48.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:48.539
2025-07-01 03:04:48.555 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:48.571 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:48.580 alo = 406, ahi = 1101
2025-07-01 03:04:48.594 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:48.606 blo = 406, bhi = 1101
2025-07-01 03:04:48.621
2025-07-01 03:04:48.630 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:48.646 r"""
2025-07-01 03:04:48.662 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:48.674 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:48.690 synch point, and intraline difference marking is done on the
2025-07-01 03:04:48.702 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:48.710
2025-07-01 03:04:48.722 Example:
2025-07-01 03:04:48.738
2025-07-01 03:04:48.746 >>> d = Differ()
2025-07-01 03:04:48.762 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:48.772 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:48.786 >>> print(''.join(results), end="")
2025-07-01 03:04:48.802 - abcDefghiJkl
2025-07-01 03:04:48.814 + abcdefGhijkl
2025-07-01 03:04:48.824 """
2025-07-01 03:04:48.829
2025-07-01 03:04:48.833 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:48.838 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:48.842 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:48.847 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:48.851 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:48.855
2025-07-01 03:04:48.860 # search for the pair that matches best without being identical
2025-07-01 03:04:48.864 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:48.869 # on junk -- unless we have to)
2025-07-01 03:04:48.873 for j in range(blo, bhi):
2025-07-01 03:04:48.877 bj = b[j]
2025-07-01 03:04:48.881 cruncher.set_seq2(bj)
2025-07-01 03:04:48.886 for i in range(alo, ahi):
2025-07-01 03:04:48.890 ai = a[i]
2025-07-01 03:04:48.894 if ai == bj:
2025-07-01 03:04:48.898 if eqi is None:
2025-07-01 03:04:48.903 eqi, eqj = i, j
2025-07-01 03:04:48.907 continue
2025-07-01 03:04:48.912 cruncher.set_seq1(ai)
2025-07-01 03:04:48.916 # computing similarity is expensive, so use the quick
2025-07-01 03:04:48.921 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:48.926 # compares by a factor of 3.
2025-07-01 03:04:48.930 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:48.935 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:48.939 # of the computation is cached by cruncher
2025-07-01 03:04:48.944 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:48.949 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:48.955 cruncher.ratio() > best_ratio:
2025-07-01 03:04:48.959 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:48.964 if best_ratio < cutoff:
2025-07-01 03:04:48.968 # no non-identical "pretty close" pair
2025-07-01 03:04:48.973 if eqi is None:
2025-07-01 03:04:48.978 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:48.982 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:48.987 return
2025-07-01 03:04:48.991 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:48.998 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:49.003 else:
2025-07-01 03:04:49.007 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:49.011 eqi = None
2025-07-01 03:04:49.016
2025-07-01 03:04:49.020 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:49.024 # identical
2025-07-01 03:04:49.029
2025-07-01 03:04:49.033 # pump out diffs from before the synch point
2025-07-01 03:04:49.037 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:49.042
2025-07-01 03:04:49.046 # do intraline marking on the synch pair
2025-07-01 03:04:49.050 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:49.055 if eqi is None:
2025-07-01 03:04:49.059 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:49.063 atags = btags = ""
2025-07-01 03:04:49.068 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:49.072 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:49.076 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:49.080 if tag == 'replace':
2025-07-01 03:04:49.085 atags += '^' * la
2025-07-01 03:04:49.089 btags += '^' * lb
2025-07-01 03:04:49.094 elif tag == 'delete':
2025-07-01 03:04:49.098 atags += '-' * la
2025-07-01 03:04:49.102 elif tag == 'insert':
2025-07-01 03:04:49.106 btags += '+' * lb
2025-07-01 03:04:49.111 elif tag == 'equal':
2025-07-01 03:04:49.115 atags += ' ' * la
2025-07-01 03:04:49.119 btags += ' ' * lb
2025-07-01 03:04:49.124 else:
2025-07-01 03:04:49.129 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:49.133 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:49.137 else:
2025-07-01 03:04:49.142 # the synch pair is identical
2025-07-01 03:04:49.146 yield '  ' + aelt
2025-07-01 03:04:49.153
2025-07-01 03:04:49.157 # pump out diffs from after the synch point
2025-07-01 03:04:49.161 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:49.166
2025-07-01 03:04:49.170 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:49.175 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:49.179
2025-07-01 03:04:49.184 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:49.188 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:49.193 alo = 407, ahi = 1101
2025-07-01 03:04:49.198 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:49.202 blo = 407, bhi = 1101
2025-07-01 03:04:49.206
2025-07-01 03:04:49.211 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:49.215 g = []
2025-07-01 03:04:49.220 if alo < ahi:
2025-07-01 03:04:49.224 if blo < bhi:
2025-07-01 03:04:49.229 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:49.233 else:
2025-07-01 03:04:49.238 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:49.242 elif blo < bhi:
2025-07-01 03:04:49.247 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:49.251
2025-07-01 03:04:49.256 >       yield from g
2025-07-01 03:04:49.260
2025-07-01 03:04:49.265 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:49.269 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:49.274
2025-07-01 03:04:49.279 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:49.284 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:49.288 alo = 407, ahi = 1101
2025-07-01 03:04:49.293 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:49.298 blo = 407, bhi = 1101
2025-07-01 03:04:49.302
2025-07-01 03:04:49.307 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:49.311 r"""
2025-07-01 03:04:49.316 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:49.321 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:49.325 synch point, and intraline difference marking is done on the
2025-07-01 03:04:49.330 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:49.335
2025-07-01 03:04:49.340 Example:
2025-07-01 03:04:49.344
2025-07-01 03:04:49.348 >>> d = Differ()
2025-07-01 03:04:49.353 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:49.358 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:49.363 >>> print(''.join(results), end="")
2025-07-01 03:04:49.367 - abcDefghiJkl
2025-07-01 03:04:49.376 + abcdefGhijkl
2025-07-01 03:04:49.385 """
2025-07-01 03:04:49.390
2025-07-01 03:04:49.394 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:49.398 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:49.403 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:49.408 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:49.412 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:49.417
2025-07-01 03:04:49.421 # search for the pair that matches best without being identical
2025-07-01 03:04:49.428 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:49.433 # on junk -- unless we have to)
2025-07-01 03:04:49.437 for j in range(blo, bhi):
2025-07-01 03:04:49.442 bj = b[j]
2025-07-01 03:04:49.446 cruncher.set_seq2(bj)
2025-07-01 03:04:49.450 for i in range(alo, ahi):
2025-07-01 03:04:49.455 ai = a[i]
2025-07-01 03:04:49.461 if ai == bj:
2025-07-01 03:04:49.466 if eqi is None:
2025-07-01 03:04:49.470 eqi, eqj = i, j
2025-07-01 03:04:49.475 continue
2025-07-01 03:04:49.479 cruncher.set_seq1(ai)
2025-07-01 03:04:49.483 # computing similarity is expensive, so use the quick
2025-07-01 03:04:49.488 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:49.492 # compares by a factor of 3.
2025-07-01 03:04:49.496 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:49.501 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:49.505 # of the computation is cached by cruncher
2025-07-01 03:04:49.509 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:49.514 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:49.518 cruncher.ratio() > best_ratio:
2025-07-01 03:04:49.523 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:49.527 if best_ratio < cutoff:
2025-07-01 03:04:49.531 # no non-identical "pretty close" pair
2025-07-01 03:04:49.535 if eqi is None:
2025-07-01 03:04:49.540 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:49.545 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:49.549 return
2025-07-01 03:04:49.553 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:49.558 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:49.563 else:
2025-07-01 03:04:49.567 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:49.571 eqi = None
2025-07-01 03:04:49.576
2025-07-01 03:04:49.581 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:49.585 # identical
2025-07-01 03:04:49.589
2025-07-01 03:04:49.594 # pump out diffs from before the synch point
2025-07-01 03:04:49.598 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:49.602
2025-07-01 03:04:49.606 # do intraline marking on the synch pair
2025-07-01 03:04:49.611 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:49.616 if eqi is None:
2025-07-01 03:04:49.622 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:49.626 atags = btags = ""
2025-07-01 03:04:49.631 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:49.635 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:49.639 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:49.643 if tag == 'replace':
2025-07-01 03:04:49.648 atags += '^' * la
2025-07-01 03:04:49.653 btags += '^' * lb
2025-07-01 03:04:49.658 elif tag == 'delete':
2025-07-01 03:04:49.663 atags += '-' * la
2025-07-01 03:04:49.667 elif tag == 'insert':
2025-07-01 03:04:49.671 btags += '+' * lb
2025-07-01 03:04:49.676 elif tag == 'equal':
2025-07-01 03:04:49.680 atags += ' ' * la
2025-07-01 03:04:49.684 btags += ' ' * lb
2025-07-01 03:04:49.688 else:
2025-07-01 03:04:49.693 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:49.698 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:49.703 else:
2025-07-01 03:04:49.707 # the synch pair is identical
2025-07-01 03:04:49.711 yield '  ' + aelt
2025-07-01 03:04:49.715
2025-07-01 03:04:49.720 # pump out diffs from after the synch point
2025-07-01 03:04:49.724 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:49.728
2025-07-01 03:04:49.733 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:49.737 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:49.741
2025-07-01 03:04:49.745 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:49.750 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:49.754 alo = 408, ahi = 1101
2025-07-01 03:04:49.759 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:49.763 blo = 408, bhi = 1101
2025-07-01 03:04:49.767
2025-07-01 03:04:49.772 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:49.776 g = []
2025-07-01 03:04:49.780 if alo < ahi:
2025-07-01 03:04:49.785 if blo < bhi:
2025-07-01 03:04:49.789 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:49.794 else:
2025-07-01 03:04:49.800 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:49.805 elif blo < bhi:
2025-07-01 03:04:49.810 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:49.815
2025-07-01 03:04:49.820 >       yield from g
2025-07-01 03:04:49.825
2025-07-01 03:04:49.830 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:49.835 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:49.840
2025-07-01 03:04:49.845 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:49.853 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:49.858 alo = 408, ahi = 1101
2025-07-01 03:04:49.863 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:49.867 blo = 408, bhi = 1101
2025-07-01 03:04:49.871
2025-07-01 03:04:49.876 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:49.880 r"""
2025-07-01 03:04:49.885 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:49.889 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:49.894 synch point, and intraline difference marking is done on the
2025-07-01 03:04:49.898 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:49.903
2025-07-01 03:04:49.907 Example:
2025-07-01 03:04:49.911
2025-07-01 03:04:49.915 >>> d = Differ()
2025-07-01 03:04:49.920 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:49.924 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:49.929 >>> print(''.join(results), end="")
2025-07-01 03:04:49.933 - abcDefghiJkl
2025-07-01 03:04:49.942 + abcdefGhijkl
2025-07-01 03:04:49.950 """
2025-07-01 03:04:49.954
2025-07-01 03:04:49.959 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:49.964 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:49.968 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:49.973 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:49.977 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:49.981
2025-07-01 03:04:49.986 # search for the pair that matches best without being identical
2025-07-01 03:04:49.990 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:49.994 # on junk -- unless we have to)
2025-07-01 03:04:50.000 for j in range(blo, bhi):
2025-07-01 03:04:50.005 bj = b[j]
2025-07-01 03:04:50.009 cruncher.set_seq2(bj)
2025-07-01 03:04:50.014 for i in range(alo, ahi):
2025-07-01 03:04:50.019 ai = a[i]
2025-07-01 03:04:50.025 if ai == bj:
2025-07-01 03:04:50.030 if eqi is None:
2025-07-01 03:04:50.035 eqi, eqj = i, j
2025-07-01 03:04:50.040 continue
2025-07-01 03:04:50.045 cruncher.set_seq1(ai)
2025-07-01 03:04:50.050 # computing similarity is expensive, so use the quick
2025-07-01 03:04:50.056 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:50.062 # compares by a factor of 3.
2025-07-01 03:04:50.069 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:50.075 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:50.084 # of the computation is cached by cruncher
2025-07-01 03:04:50.092 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:50.098 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:50.104 cruncher.ratio() > best_ratio:
2025-07-01 03:04:50.108 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:50.113 if best_ratio < cutoff:
2025-07-01 03:04:50.118 # no non-identical "pretty close" pair
2025-07-01 03:04:50.122 if eqi is None:
2025-07-01 03:04:50.127 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:50.131 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:50.139 return
2025-07-01 03:04:50.154 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:50.170 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:50.178 else:
2025-07-01 03:04:50.190 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:50.206 eqi = None
2025-07-01 03:04:50.218
2025-07-01 03:04:50.230 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:50.246 # identical
2025-07-01 03:04:50.255
2025-07-01 03:04:50.266 # pump out diffs from before the synch point
2025-07-01 03:04:50.278 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:50.286
2025-07-01 03:04:50.298 # do intraline marking on the synch pair
2025-07-01 03:04:50.308 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:50.319 if eqi is None:
2025-07-01 03:04:50.334 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:50.341 atags = btags = ""
2025-07-01 03:04:50.356 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:50.365 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:50.374 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:50.389 if tag == 'replace':
2025-07-01 03:04:50.402 atags += '^' * la
2025-07-01 03:04:50.409 btags += '^' * lb
2025-07-01 03:04:50.425 elif tag == 'delete':
2025-07-01 03:04:50.429 atags += '-' * la
2025-07-01 03:04:50.449 elif tag == 'insert':
2025-07-01 03:04:50.461 btags += '+' * lb
2025-07-01 03:04:50.469 elif tag == 'equal':
2025-07-01 03:04:50.478 atags += ' ' * la
2025-07-01 03:04:50.490 btags += ' ' * lb
2025-07-01 03:04:50.499 else:
2025-07-01 03:04:50.516 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:50.534 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:50.541 else:
2025-07-01 03:04:50.547 # the synch pair is identical
2025-07-01 03:04:50.552 yield '  ' + aelt
2025-07-01 03:04:50.562
2025-07-01 03:04:50.576 # pump out diffs from after the synch point
2025-07-01 03:04:50.586 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:50.598
2025-07-01 03:04:50.610 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:50.620 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:50.624
2025-07-01 03:04:50.628 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:50.634 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:50.649 alo = 409, ahi = 1101
2025-07-01 03:04:50.662 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:50.674 blo = 409, bhi = 1101
2025-07-01 03:04:50.682
2025-07-01 03:04:50.694 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:50.705 g = []
2025-07-01 03:04:50.718 if alo < ahi:
2025-07-01 03:04:50.730 if blo < bhi:
2025-07-01 03:04:50.742 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:50.754 else:
2025-07-01 03:04:50.762 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:50.774 elif blo < bhi:
2025-07-01 03:04:50.784 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:50.798
2025-07-01 03:04:50.806 >       yield from g
2025-07-01 03:04:50.822
2025-07-01 03:04:50.829 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:50.842 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:50.854
2025-07-01 03:04:50.862 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:50.874 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:50.882 alo = 409, ahi = 1101
2025-07-01 03:04:50.894 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:50.906 blo = 409, bhi = 1101
2025-07-01 03:04:50.914
2025-07-01 03:04:50.930 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:50.942 r"""
2025-07-01 03:04:50.952 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:50.970 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:50.978 synch point, and intraline difference marking is done on the
2025-07-01 03:04:50.990 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:50.998
2025-07-01 03:04:51.007 Example:
2025-07-01 03:04:51.018
2025-07-01 03:04:51.030 >>> d = Differ()
2025-07-01 03:04:51.038 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:51.049 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:51.066 >>> print(''.join(results), end="")
2025-07-01 03:04:51.074 - abcDefghiJkl
2025-07-01 03:04:51.094 + abcdefGhijkl
2025-07-01 03:04:51.114 """
2025-07-01 03:04:51.122
2025-07-01 03:04:51.134 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:51.141 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:51.154 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:51.165 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:51.174 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:51.185
2025-07-01 03:04:51.193 # search for the pair that matches best without being identical
2025-07-01 03:04:51.206 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:51.218 # on junk -- unless we have to)
2025-07-01 03:04:51.230 for j in range(blo, bhi):
2025-07-01 03:04:51.242 bj = b[j]
2025-07-01 03:04:51.250 cruncher.set_seq2(bj)
2025-07-01 03:04:51.269 for i in range(alo, ahi):
2025-07-01 03:04:51.281 ai = a[i]
2025-07-01 03:04:51.289 if ai == bj:
2025-07-01 03:04:51.304 if eqi is None:
2025-07-01 03:04:51.314 eqi, eqj = i, j
2025-07-01 03:04:51.321 continue
2025-07-01 03:04:51.330 cruncher.set_seq1(ai)
2025-07-01 03:04:51.339 # computing similarity is expensive, so use the quick
2025-07-01 03:04:51.352 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:51.366 # compares by a factor of 3.
2025-07-01 03:04:51.373 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:51.386 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:51.393 # of the computation is cached by cruncher
2025-07-01 03:04:51.404 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:51.418 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:51.426 cruncher.ratio() > best_ratio:
2025-07-01 03:04:51.433 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:51.448 if best_ratio < cutoff:
2025-07-01 03:04:51.466 # no non-identical "pretty close" pair
2025-07-01 03:04:51.475 if eqi is None:
2025-07-01 03:04:51.485 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:51.496 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:51.506 return
2025-07-01 03:04:51.517 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:51.526 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:51.534 else:
2025-07-01 03:04:51.550 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:51.557 eqi = None
2025-07-01 03:04:51.572
2025-07-01 03:04:51.581 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:51.594 # identical
2025-07-01 03:04:51.606
2025-07-01 03:04:51.618 # pump out diffs from before the synch point
2025-07-01 03:04:51.627 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:51.640
2025-07-01 03:04:51.658 # do intraline marking on the synch pair
2025-07-01 03:04:51.674 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:51.686 if eqi is None:
2025-07-01 03:04:51.698 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:51.714 atags = btags = ""
2025-07-01 03:04:51.730 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:51.738 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:51.750 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:51.758 if tag == 'replace':
2025-07-01 03:04:51.770 atags += '^' * la
2025-07-01 03:04:51.777 btags += '^' * lb
2025-07-01 03:04:51.790 elif tag == 'delete':
2025-07-01 03:04:51.802 atags += '-' * la
2025-07-01 03:04:51.810 elif tag == 'insert':
2025-07-01 03:04:51.820 btags += '+' * lb
2025-07-01 03:04:51.834 elif tag == 'equal':
2025-07-01 03:04:51.842 atags += ' ' * la
2025-07-01 03:04:51.854 btags += ' ' * lb
2025-07-01 03:04:51.862 else:
2025-07-01 03:04:51.874 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:51.886 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:51.894 else:
2025-07-01 03:04:51.906 # the synch pair is identical
2025-07-01 03:04:51.914 yield '  ' + aelt
2025-07-01 03:04:51.926
2025-07-01 03:04:51.933 # pump out diffs from after the synch point
2025-07-01 03:04:51.946 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:51.956
2025-07-01 03:04:51.970 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:51.982 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:51.990
2025-07-01 03:04:52.002 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:52.018 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:52.026 alo = 410, ahi = 1101
2025-07-01 03:04:52.046 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:52.058 blo = 410, bhi = 1101
2025-07-01 03:04:52.066
2025-07-01 03:04:52.078 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:52.090 g = []
2025-07-01 03:04:52.098 if alo < ahi:
2025-07-01 03:04:52.110 if blo < bhi:
2025-07-01 03:04:52.122 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:52.130 else:
2025-07-01 03:04:52.146 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:52.162 elif blo < bhi:
2025-07-01 03:04:52.178 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:52.195
2025-07-01 03:04:52.206 >       yield from g
2025-07-01 03:04:52.223
2025-07-01 03:04:52.238 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:52.245 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:52.251
2025-07-01 03:04:52.256 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:52.262 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:52.267 alo = 410, ahi = 1101
2025-07-01 03:04:52.274 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:52.279 blo = 410, bhi = 1101
2025-07-01 03:04:52.285
2025-07-01 03:04:52.290 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:52.295 r"""
2025-07-01 03:04:52.300 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:52.305 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:52.311 synch point, and intraline difference marking is done on the
2025-07-01 03:04:52.316 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:52.321
2025-07-01 03:04:52.327 Example:
2025-07-01 03:04:52.333
2025-07-01 03:04:52.338 >>> d = Differ()
2025-07-01 03:04:52.342 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:52.347 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:52.351 >>> print(''.join(results), end="")
2025-07-01 03:04:52.356 - abcDefghiJkl
2025-07-01 03:04:52.365 + abcdefGhijkl
2025-07-01 03:04:52.382 """
2025-07-01 03:04:52.390
2025-07-01 03:04:52.406 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:52.422 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:52.438 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:52.454 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:52.462 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:52.474
2025-07-01 03:04:52.482 # search for the pair that matches best without being identical
2025-07-01 03:04:52.494 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:52.501 # on junk -- unless we have to)
2025-07-01 03:04:52.509 for j in range(blo, bhi):
2025-07-01 03:04:52.516 bj = b[j]
2025-07-01 03:04:52.524 cruncher.set_seq2(bj)
2025-07-01 03:04:52.530 for i in range(alo, ahi):
2025-07-01 03:04:52.545 ai = a[i]
2025-07-01 03:04:52.554 if ai == bj:
2025-07-01 03:04:52.562 if eqi is None:
2025-07-01 03:04:52.574 eqi, eqj = i, j
2025-07-01 03:04:52.594 continue
2025-07-01 03:04:52.602 cruncher.set_seq1(ai)
2025-07-01 03:04:52.614 # computing similarity is expensive, so use the quick
2025-07-01 03:04:52.622 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:52.634 # compares by a factor of 3.
2025-07-01 03:04:52.642 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:52.654 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:52.667 # of the computation is cached by cruncher
2025-07-01 03:04:52.674 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:52.686 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:52.698 cruncher.ratio() > best_ratio:
2025-07-01 03:04:52.706 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:52.718 if best_ratio < cutoff:
2025-07-01 03:04:52.730 # no non-identical "pretty close" pair
2025-07-01 03:04:52.742 if eqi is None:
2025-07-01 03:04:52.762 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:52.770 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:52.778 return
2025-07-01 03:04:52.790 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:52.807 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:52.814 else:
2025-07-01 03:04:52.822 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:52.834 eqi = None
2025-07-01 03:04:52.850
2025-07-01 03:04:52.858 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:52.870 # identical
2025-07-01 03:04:52.882
2025-07-01 03:04:52.894 # pump out diffs from before the synch point
2025-07-01 03:04:52.911 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:52.922
2025-07-01 03:04:52.932 # do intraline marking on the synch pair
2025-07-01 03:04:52.946 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:52.957 if eqi is None:
2025-07-01 03:04:52.968 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:52.980 atags = btags = ""
2025-07-01 03:04:52.998 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:53.006 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:53.014 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:53.029 if tag == 'replace':
2025-07-01 03:04:53.041 atags += '^' * la
2025-07-01 03:04:53.061 btags += '^' * lb
2025-07-01 03:04:53.075 elif tag == 'delete':
2025-07-01 03:04:53.087 atags += '-' * la
2025-07-01 03:04:53.108 elif tag == 'insert':
2025-07-01 03:04:53.118 btags += '+' * lb
2025-07-01 03:04:53.139 elif tag == 'equal':
2025-07-01 03:04:53.154 atags += ' ' * la
2025-07-01 03:04:53.166 btags += ' ' * lb
2025-07-01 03:04:53.174 else:
2025-07-01 03:04:53.186 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:53.203 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:53.218 else:
2025-07-01 03:04:53.230 # the synch pair is identical
2025-07-01 03:04:53.242 yield '  ' + aelt
2025-07-01 03:04:53.254
2025-07-01 03:04:53.262 # pump out diffs from after the synch point
2025-07-01 03:04:53.274 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:53.282
2025-07-01 03:04:53.294 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:53.310 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:53.322
2025-07-01 03:04:53.330 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:53.342 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:53.354 alo = 411, ahi = 1101
2025-07-01 03:04:53.366 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:53.374 blo = 411, bhi = 1101
2025-07-01 03:04:53.386
2025-07-01 03:04:53.398 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:53.406 g = []
2025-07-01 03:04:53.418 if alo < ahi:
2025-07-01 03:04:53.426 if blo < bhi:
2025-07-01 03:04:53.438 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:53.450 else:
2025-07-01 03:04:53.458 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:53.473 elif blo < bhi:
2025-07-01 03:04:53.482 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:53.494
2025-07-01 03:04:53.506 >       yield from g
2025-07-01 03:04:53.521
2025-07-01 03:04:53.534 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:53.542 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:53.554
2025-07-01 03:04:53.562 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:53.573 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:53.587 alo = 411, ahi = 1101
2025-07-01 03:04:53.602 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:53.610 blo = 411, bhi = 1101
2025-07-01 03:04:53.626
2025-07-01 03:04:53.642 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:53.650 r"""
2025-07-01 03:04:53.657 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:53.666 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:53.678 synch point, and intraline difference marking is done on the
2025-07-01 03:04:53.686 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:53.701
2025-07-01 03:04:53.714 Example:
2025-07-01 03:04:53.726
2025-07-01 03:04:53.738 >>> d = Differ()
2025-07-01 03:04:53.746 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:53.758 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:53.766 >>> print(''.join(results), end="")
2025-07-01 03:04:53.778 - abcDefghiJkl
2025-07-01 03:04:53.794 + abcdefGhijkl
2025-07-01 03:04:53.814 """
2025-07-01 03:04:53.826
2025-07-01 03:04:53.834 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:53.849 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:53.858 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:53.871 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:53.881 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:53.890
2025-07-01 03:04:53.910 # search for the pair that matches best without being identical
2025-07-01 03:04:53.918 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:53.934 # on junk -- unless we have to)
2025-07-01 03:04:53.941 for j in range(blo, bhi):
2025-07-01 03:04:53.949 bj = b[j]
2025-07-01 03:04:53.964 cruncher.set_seq2(bj)
2025-07-01 03:04:53.973 for i in range(alo, ahi):
2025-07-01 03:04:53.990 ai = a[i]
2025-07-01 03:04:53.998 if ai == bj:
2025-07-01 03:04:54.014 if eqi is None:
2025-07-01 03:04:54.022 eqi, eqj = i, j
2025-07-01 03:04:54.038 continue
2025-07-01 03:04:54.046 cruncher.set_seq1(ai)
2025-07-01 03:04:54.058 # computing similarity is expensive, so use the quick
2025-07-01 03:04:54.074 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:54.086 # compares by a factor of 3.
2025-07-01 03:04:54.098 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:54.106 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:54.116 # of the computation is cached by cruncher
2025-07-01 03:04:54.126 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:54.138 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:54.146 cruncher.ratio() > best_ratio:
2025-07-01 03:04:54.161 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:54.170 if best_ratio < cutoff:
2025-07-01 03:04:54.185 # no non-identical "pretty close" pair
2025-07-01 03:04:54.194 if eqi is None:
2025-07-01 03:04:54.206 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:54.214 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:54.226 return
2025-07-01 03:04:54.233 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:54.246 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:54.258 else:
2025-07-01 03:04:54.272 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:54.282 eqi = None
2025-07-01 03:04:54.292
2025-07-01 03:04:54.306 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:54.316 # identical
2025-07-01 03:04:54.334
2025-07-01 03:04:54.344 # pump out diffs from before the synch point
2025-07-01 03:04:54.356 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:54.366
2025-07-01 03:04:54.374 # do intraline marking on the synch pair
2025-07-01 03:04:54.390 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:54.398 if eqi is None:
2025-07-01 03:04:54.413 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:54.422 atags = btags = ""
2025-07-01 03:04:54.434 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:54.446 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:54.462 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:54.474 if tag == 'replace':
2025-07-01 03:04:54.481 atags += '^' * la
2025-07-01 03:04:54.494 btags += '^' * lb
2025-07-01 03:04:54.502 elif tag == 'delete':
2025-07-01 03:04:54.514 atags += '-' * la
2025-07-01 03:04:54.521 elif tag == 'insert':
2025-07-01 03:04:54.529 btags += '+' * lb
2025-07-01 03:04:54.545 elif tag == 'equal':
2025-07-01 03:04:54.558 atags += ' ' * la
2025-07-01 03:04:54.566 btags += ' ' * lb
2025-07-01 03:04:54.574 else:
2025-07-01 03:04:54.588 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:54.598 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:54.610 else:
2025-07-01 03:04:54.620 # the synch pair is identical
2025-07-01 03:04:54.634 yield '  ' + aelt
2025-07-01 03:04:54.646
2025-07-01 03:04:54.654 # pump out diffs from after the synch point
2025-07-01 03:04:54.666 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:54.678
2025-07-01 03:04:54.694 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:54.702 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:54.716
2025-07-01 03:04:54.729 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:54.746 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:54.754 alo = 412, ahi = 1101
2025-07-01 03:04:54.766 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:54.777 blo = 412, bhi = 1101
2025-07-01 03:04:54.794
2025-07-01 03:04:54.806 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:54.813 g = []
2025-07-01 03:04:54.821 if alo < ahi:
2025-07-01 03:04:54.834 if blo < bhi:
2025-07-01 03:04:54.845 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:54.858 else:
2025-07-01 03:04:54.870 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:54.878 elif blo < bhi:
2025-07-01 03:04:54.890 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:54.897
2025-07-01 03:04:54.908 >       yield from g
2025-07-01 03:04:54.921
2025-07-01 03:04:54.934 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:54.946 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:54.958
2025-07-01 03:04:54.966 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:54.981 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:54.994 alo = 412, ahi = 1101
2025-07-01 03:04:55.003 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:55.022 blo = 412, bhi = 1101
2025-07-01 03:04:55.034
2025-07-01 03:04:55.046 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:55.057 r"""
2025-07-01 03:04:55.070 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:55.077 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:55.089 synch point, and intraline difference marking is done on the
2025-07-01 03:04:55.102 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:55.113
2025-07-01 03:04:55.122 Example:
2025-07-01 03:04:55.134
2025-07-01 03:04:55.141 >>> d = Differ()
2025-07-01 03:04:55.154 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:55.162 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:55.174 >>> print(''.join(results), end="")
2025-07-01 03:04:55.186 - abcDefghiJkl
2025-07-01 03:04:55.214 + abcdefGhijkl
2025-07-01 03:04:55.234 """
2025-07-01 03:04:55.246
2025-07-01 03:04:55.261 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:55.272 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:55.282 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:55.296 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:55.310 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:55.320
2025-07-01 03:04:55.330 # search for the pair that matches best without being identical
2025-07-01 03:04:55.338 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:55.346 # on junk -- unless we have to)
2025-07-01 03:04:55.358 for j in range(blo, bhi):
2025-07-01 03:04:55.371 bj = b[j]
2025-07-01 03:04:55.382 cruncher.set_seq2(bj)
2025-07-01 03:04:55.390 for i in range(alo, ahi):
2025-07-01 03:04:55.401 ai = a[i]
2025-07-01 03:04:55.407 if ai == bj:
2025-07-01 03:04:55.426 if eqi is None:
2025-07-01 03:04:55.437 eqi, eqj = i, j
2025-07-01 03:04:55.448 continue
2025-07-01 03:04:55.456 cruncher.set_seq1(ai)
2025-07-01 03:04:55.461 # computing similarity is expensive, so use the quick
2025-07-01 03:04:55.466 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:55.470 # compares by a factor of 3.
2025-07-01 03:04:55.475 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:55.480 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:55.485 # of the computation is cached by cruncher
2025-07-01 03:04:55.489 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:55.497 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:55.508 cruncher.ratio() > best_ratio:
2025-07-01 03:04:55.523 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:55.528 if best_ratio < cutoff:
2025-07-01 03:04:55.549 # no non-identical "pretty close" pair
2025-07-01 03:04:55.557 if eqi is None:
2025-07-01 03:04:55.566 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:55.583 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:55.595 return
2025-07-01 03:04:55.609 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:55.619 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:55.633 else:
2025-07-01 03:04:55.642 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:55.655 eqi = None
2025-07-01 03:04:55.676
2025-07-01 03:04:55.695 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:55.706 # identical
2025-07-01 03:04:55.729
2025-07-01 03:04:55.745 # pump out diffs from before the synch point
2025-07-01 03:04:55.765 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:55.774
2025-07-01 03:04:55.785 # do intraline marking on the synch pair
2025-07-01 03:04:55.801 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:55.811 if eqi is None:
2025-07-01 03:04:55.833 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:55.847 atags = btags = ""
2025-07-01 03:04:55.862 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:55.883 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:55.903 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:55.910 if tag == 'replace':
2025-07-01 03:04:55.923 atags += '^' * la
2025-07-01 03:04:55.937 btags += '^' * lb
2025-07-01 03:04:55.951 elif tag == 'delete':
2025-07-01 03:04:55.966 atags += '-' * la
2025-07-01 03:04:55.978 elif tag == 'insert':
2025-07-01 03:04:55.995 btags += '+' * lb
2025-07-01 03:04:56.014 elif tag == 'equal':
2025-07-01 03:04:56.026 atags += ' ' * la
2025-07-01 03:04:56.043 btags += ' ' * lb
2025-07-01 03:04:56.056 else:
2025-07-01 03:04:56.079 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:56.094 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:56.110 else:
2025-07-01 03:04:56.118 # the synch pair is identical
2025-07-01 03:04:56.130 yield '  ' + aelt
2025-07-01 03:04:56.146
2025-07-01 03:04:56.156 # pump out diffs from after the synch point
2025-07-01 03:04:56.174 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:56.190
2025-07-01 03:04:56.198 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:56.210 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:56.224
2025-07-01 03:04:56.234 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:56.250 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:56.262 alo = 413, ahi = 1101
2025-07-01 03:04:56.274 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:56.286 blo = 413, bhi = 1101
2025-07-01 03:04:56.294
2025-07-01 03:04:56.306 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:56.313 g = []
2025-07-01 03:04:56.330 if alo < ahi:
2025-07-01 03:04:56.338 if blo < bhi:
2025-07-01 03:04:56.350 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:56.365 else:
2025-07-01 03:04:56.382 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:56.390 elif blo < bhi:
2025-07-01 03:04:56.406 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:56.416
2025-07-01 03:04:56.433 >       yield from g
2025-07-01 03:04:56.442
2025-07-01 03:04:56.452 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:56.462 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:56.476
2025-07-01 03:04:56.486 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:56.502 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:56.510 alo = 413, ahi = 1101
2025-07-01 03:04:56.518 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:56.530 blo = 413, bhi = 1101
2025-07-01 03:04:56.538
2025-07-01 03:04:56.550 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:56.558 r"""
2025-07-01 03:04:56.570 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:56.582 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:56.590 synch point, and intraline difference marking is done on the
2025-07-01 03:04:56.602 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:56.614
2025-07-01 03:04:56.626 Example:
2025-07-01 03:04:56.634
2025-07-01 03:04:56.646 >>> d = Differ()
2025-07-01 03:04:56.654 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:56.666 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:56.678 >>> print(''.join(results), end="")
2025-07-01 03:04:56.686 - abcDefghiJkl
2025-07-01 03:04:56.706 + abcdefGhijkl
2025-07-01 03:04:56.726 """
2025-07-01 03:04:56.732
2025-07-01 03:04:56.746 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:56.758 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:56.765 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:56.778 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:56.790 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:56.797
2025-07-01 03:04:56.808 # search for the pair that matches best without being identical
2025-07-01 03:04:56.818 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:56.830 # on junk -- unless we have to)
2025-07-01 03:04:56.838 for j in range(blo, bhi):
2025-07-01 03:04:56.850 bj = b[j]
2025-07-01 03:04:56.857 cruncher.set_seq2(bj)
2025-07-01 03:04:56.872 for i in range(alo, ahi):
2025-07-01 03:04:56.882 ai = a[i]
2025-07-01 03:04:56.894 if ai == bj:
2025-07-01 03:04:56.910 if eqi is None:
2025-07-01 03:04:56.918 eqi, eqj = i, j
2025-07-01 03:04:56.930 continue
2025-07-01 03:04:56.938 cruncher.set_seq1(ai)
2025-07-01 03:04:56.950 # computing similarity is expensive, so use the quick
2025-07-01 03:04:56.958 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:56.970 # compares by a factor of 3.
2025-07-01 03:04:56.982 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:56.996 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:57.010 # of the computation is cached by cruncher
2025-07-01 03:04:57.022 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:57.034 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:57.046 cruncher.ratio() > best_ratio:
2025-07-01 03:04:57.054 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:57.065 if best_ratio < cutoff:
2025-07-01 03:04:57.077 # no non-identical "pretty close" pair
2025-07-01 03:04:57.090 if eqi is None:
2025-07-01 03:04:57.098 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:57.112 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:57.127 return
2025-07-01 03:04:57.142 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:57.150 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:57.162 else:
2025-07-01 03:04:57.180 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:57.194 eqi = None
2025-07-01 03:04:57.210
2025-07-01 03:04:57.222 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:57.229 # identical
2025-07-01 03:04:57.246
2025-07-01 03:04:57.260 # pump out diffs from before the synch point
2025-07-01 03:04:57.265 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:57.278
2025-07-01 03:04:57.283 # do intraline marking on the synch pair
2025-07-01 03:04:57.293 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:57.308 if eqi is None:
2025-07-01 03:04:57.321 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:57.331 atags = btags = ""
2025-07-01 03:04:57.341 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:57.352 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:57.365 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:57.373 if tag == 'replace':
2025-07-01 03:04:57.385 atags += '^' * la
2025-07-01 03:04:57.393 btags += '^' * lb
2025-07-01 03:04:57.402 elif tag == 'delete':
2025-07-01 03:04:57.413 atags += '-' * la
2025-07-01 03:04:57.430 elif tag == 'insert':
2025-07-01 03:04:57.438 btags += '+' * lb
2025-07-01 03:04:57.446 elif tag == 'equal':
2025-07-01 03:04:57.453 atags += ' ' * la
2025-07-01 03:04:57.469 btags += ' ' * lb
2025-07-01 03:04:57.482 else:
2025-07-01 03:04:57.498 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:57.507 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:57.518 else:
2025-07-01 03:04:57.538 # the synch pair is identical
2025-07-01 03:04:57.546 yield '  ' + aelt
2025-07-01 03:04:57.554
2025-07-01 03:04:57.566 # pump out diffs from after the synch point
2025-07-01 03:04:57.578 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:57.590
2025-07-01 03:04:57.598 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:57.610 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:57.618
2025-07-01 03:04:57.630 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:57.642 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:57.654 alo = 414, ahi = 1101
2025-07-01 03:04:57.670 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:57.678 blo = 414, bhi = 1101
2025-07-01 03:04:57.692
2025-07-01 03:04:57.706 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:57.714 g = []
2025-07-01 03:04:57.722 if alo < ahi:
2025-07-01 03:04:57.734 if blo < bhi:
2025-07-01 03:04:57.742 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:57.752 else:
2025-07-01 03:04:57.761 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:57.772 elif blo < bhi:
2025-07-01 03:04:57.786 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:57.798
2025-07-01 03:04:57.810 >       yield from g
2025-07-01 03:04:57.822
2025-07-01 03:04:57.830 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:57.838 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:57.849
2025-07-01 03:04:57.858 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:57.873 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:57.882 alo = 414, ahi = 1101
2025-07-01 03:04:57.890 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:57.906 blo = 414, bhi = 1101
2025-07-01 03:04:57.920
2025-07-01 03:04:57.925 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:57.938 r"""
2025-07-01 03:04:57.956 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:57.976 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:57.990 synch point, and intraline difference marking is done on the
2025-07-01 03:04:57.998 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:58.010
2025-07-01 03:04:58.018 Example:
2025-07-01 03:04:58.030
2025-07-01 03:04:58.046 >>> d = Differ()
2025-07-01 03:04:58.054 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:58.065 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:58.082 >>> print(''.join(results), end="")
2025-07-01 03:04:58.092 - abcDefghiJkl
2025-07-01 03:04:58.114 + abcdefGhijkl
2025-07-01 03:04:58.134 """
2025-07-01 03:04:58.142
2025-07-01 03:04:58.154 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:58.166 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:58.174 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:58.186 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:58.198 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:58.206
2025-07-01 03:04:58.222 # search for the pair that matches best without being identical
2025-07-01 03:04:58.229 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:58.242 # on junk -- unless we have to)
2025-07-01 03:04:58.249 for j in range(blo, bhi):
2025-07-01 03:04:58.265 bj = b[j]
2025-07-01 03:04:58.280 cruncher.set_seq2(bj)
2025-07-01 03:04:58.290 for i in range(alo, ahi):
2025-07-01 03:04:58.304 ai = a[i]
2025-07-01 03:04:58.318 if ai == bj:
2025-07-01 03:04:58.330 if eqi is None:
2025-07-01 03:04:58.338 eqi, eqj = i, j
2025-07-01 03:04:58.349 continue
2025-07-01 03:04:58.357 cruncher.set_seq1(ai)
2025-07-01 03:04:58.374 # computing similarity is expensive, so use the quick
2025-07-01 03:04:58.382 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:58.394 # compares by a factor of 3.
2025-07-01 03:04:58.406 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:58.414 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:58.430 # of the computation is cached by cruncher
2025-07-01 03:04:58.438 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:58.454 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:58.464 cruncher.ratio() > best_ratio:
2025-07-01 03:04:58.481 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:58.490 if best_ratio < cutoff:
2025-07-01 03:04:58.505 # no non-identical "pretty close" pair
2025-07-01 03:04:58.514 if eqi is None:
2025-07-01 03:04:58.525 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:58.538 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:58.548 return
2025-07-01 03:04:58.555 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:58.568 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:58.580 else:
2025-07-01 03:04:58.598 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:58.610 eqi = None
2025-07-01 03:04:58.626
2025-07-01 03:04:58.634 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:58.645 # identical
2025-07-01 03:04:58.657
2025-07-01 03:04:58.669 # pump out diffs from before the synch point
2025-07-01 03:04:58.683 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:58.697
2025-07-01 03:04:58.714 # do intraline marking on the synch pair
2025-07-01 03:04:58.726 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:58.741 if eqi is None:
2025-07-01 03:04:58.752 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:58.770 atags = btags = ""
2025-07-01 03:04:58.789 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:58.798 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:58.810 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:58.822 if tag == 'replace':
2025-07-01 03:04:58.829 atags += '^' * la
2025-07-01 03:04:58.838 btags += '^' * lb
2025-07-01 03:04:58.853 elif tag == 'delete':
2025-07-01 03:04:58.866 atags += '-' * la
2025-07-01 03:04:58.874 elif tag == 'insert':
2025-07-01 03:04:58.881 btags += '+' * lb
2025-07-01 03:04:58.898 elif tag == 'equal':
2025-07-01 03:04:58.906 atags += ' ' * la
2025-07-01 03:04:58.918 btags += ' ' * lb
2025-07-01 03:04:58.926 else:
2025-07-01 03:04:58.942 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:58.950 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:58.962 else:
2025-07-01 03:04:58.970 # the synch pair is identical
2025-07-01 03:04:58.982 yield '  ' + aelt
2025-07-01 03:04:58.990
2025-07-01 03:04:59.006 # pump out diffs from after the synch point
2025-07-01 03:04:59.026 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:59.034
2025-07-01 03:04:59.054 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:59.066 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:59.074
2025-07-01 03:04:59.086 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:59.098 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:59.106 alo = 415, ahi = 1101
2025-07-01 03:04:59.122 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:59.135 blo = 415, bhi = 1101
2025-07-01 03:04:59.146
2025-07-01 03:04:59.157 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:59.170 g = []
2025-07-01 03:04:59.186 if alo < ahi:
2025-07-01 03:04:59.202 if blo < bhi:
2025-07-01 03:04:59.214 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:59.228 else:
2025-07-01 03:04:59.238 g = self._dump('-', a, alo, ahi)
2025-07-01 03:04:59.254 elif blo < bhi:
2025-07-01 03:04:59.265 g = self._dump('+', b, blo, bhi)
2025-07-01 03:04:59.276
2025-07-01 03:04:59.298 >       yield from g
2025-07-01 03:04:59.310
2025-07-01 03:04:59.320 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:04:59.325 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:59.330
2025-07-01 03:04:59.335 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:59.341 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:59.345 alo = 415, ahi = 1101
2025-07-01 03:04:59.351 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:59.355 blo = 415, bhi = 1101
2025-07-01 03:04:59.359
2025-07-01 03:04:59.364 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:59.368 r"""
2025-07-01 03:04:59.373 When replacing one block of lines with another, search the blocks
2025-07-01 03:04:59.377 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:04:59.382 synch point, and intraline difference marking is done on the
2025-07-01 03:04:59.386 similar pair. Lots of work, but often worth it.
2025-07-01 03:04:59.391
2025-07-01 03:04:59.395 Example:
2025-07-01 03:04:59.399
2025-07-01 03:04:59.404 >>> d = Differ()
2025-07-01 03:04:59.408 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:04:59.412 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:04:59.417 >>> print(''.join(results), end="")
2025-07-01 03:04:59.422 - abcDefghiJkl
2025-07-01 03:04:59.430 + abcdefGhijkl
2025-07-01 03:04:59.439 """
2025-07-01 03:04:59.444
2025-07-01 03:04:59.449 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:04:59.453 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:04:59.458 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:04:59.463 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:04:59.467 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:04:59.471
2025-07-01 03:04:59.476 # search for the pair that matches best without being identical
2025-07-01 03:04:59.481 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:04:59.486 # on junk -- unless we have to)
2025-07-01 03:04:59.490 for j in range(blo, bhi):
2025-07-01 03:04:59.495 bj = b[j]
2025-07-01 03:04:59.499 cruncher.set_seq2(bj)
2025-07-01 03:04:59.504 for i in range(alo, ahi):
2025-07-01 03:04:59.508 ai = a[i]
2025-07-01 03:04:59.517 if ai == bj:
2025-07-01 03:04:59.522 if eqi is None:
2025-07-01 03:04:59.526 eqi, eqj = i, j
2025-07-01 03:04:59.530 continue
2025-07-01 03:04:59.535 cruncher.set_seq1(ai)
2025-07-01 03:04:59.541 # computing similarity is expensive, so use the quick
2025-07-01 03:04:59.546 # upper bounds first -- have seen this speed up messy
2025-07-01 03:04:59.552 # compares by a factor of 3.
2025-07-01 03:04:59.556 # note that ratio() is only expensive to compute the first
2025-07-01 03:04:59.561 # time it's called on a sequence pair; the expensive part
2025-07-01 03:04:59.565 # of the computation is cached by cruncher
2025-07-01 03:04:59.573 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:04:59.582 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:04:59.590 cruncher.ratio() > best_ratio:
2025-07-01 03:04:59.602 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:04:59.616 if best_ratio < cutoff:
2025-07-01 03:04:59.630 # no non-identical "pretty close" pair
2025-07-01 03:04:59.638 if eqi is None:
2025-07-01 03:04:59.646 # no identical pair either -- treat it as a straight replace
2025-07-01 03:04:59.662 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:04:59.670 return
2025-07-01 03:04:59.686 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:04:59.694 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:04:59.706 else:
2025-07-01 03:04:59.721 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:04:59.729 eqi = None
2025-07-01 03:04:59.734
2025-07-01 03:04:59.739 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:04:59.743 # identical
2025-07-01 03:04:59.747
2025-07-01 03:04:59.752 # pump out diffs from before the synch point
2025-07-01 03:04:59.757 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:04:59.761
2025-07-01 03:04:59.766 # do intraline marking on the synch pair
2025-07-01 03:04:59.770 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:04:59.775 if eqi is None:
2025-07-01 03:04:59.779 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:04:59.783 atags = btags = ""
2025-07-01 03:04:59.788 cruncher.set_seqs(aelt, belt)
2025-07-01 03:04:59.793 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:04:59.797 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:04:59.801 if tag == 'replace':
2025-07-01 03:04:59.805 atags += '^' * la
2025-07-01 03:04:59.810 btags += '^' * lb
2025-07-01 03:04:59.814 elif tag == 'delete':
2025-07-01 03:04:59.819 atags += '-' * la
2025-07-01 03:04:59.823 elif tag == 'insert':
2025-07-01 03:04:59.828 btags += '+' * lb
2025-07-01 03:04:59.832 elif tag == 'equal':
2025-07-01 03:04:59.836 atags += ' ' * la
2025-07-01 03:04:59.841 btags += ' ' * lb
2025-07-01 03:04:59.845 else:
2025-07-01 03:04:59.850 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:04:59.854 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:04:59.859 else:
2025-07-01 03:04:59.865 # the synch pair is identical
2025-07-01 03:04:59.869 yield '  ' + aelt
2025-07-01 03:04:59.875
2025-07-01 03:04:59.880 # pump out diffs from after the synch point
2025-07-01 03:04:59.887 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:04:59.899
2025-07-01 03:04:59.904 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:04:59.908 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:04:59.913
2025-07-01 03:04:59.917 self = <difflib.Differ object at [hex]>
2025-07-01 03:04:59.926 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:04:59.937 alo = 416, ahi = 1101
2025-07-01 03:04:59.949 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:04:59.958 blo = 416, bhi = 1101
2025-07-01 03:04:59.973
2025-07-01 03:04:59.982 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:04:59.992 g = []
2025-07-01 03:05:00.005 if alo < ahi:
2025-07-01 03:05:00.016 if blo < bhi:
2025-07-01 03:05:00.023 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:00.042 else:
2025-07-01 03:05:00.056 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:00.070 elif blo < bhi:
2025-07-01 03:05:00.084 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:00.094
2025-07-01 03:05:00.106 >       yield from g
2025-07-01 03:05:00.118
2025-07-01 03:05:00.130 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:00.142 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:00.158
2025-07-01 03:05:00.174 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:00.182 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:00.194 alo = 416, ahi = 1101
2025-07-01 03:05:00.206 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:00.222 blo = 416, bhi = 1101
2025-07-01 03:05:00.234
2025-07-01 03:05:00.250 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:00.266 r"""
2025-07-01 03:05:00.274 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:00.290 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:00.306 synch point, and intraline difference marking is done on the
2025-07-01 03:05:00.322 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:00.334
2025-07-01 03:05:00.342 Example:
2025-07-01 03:05:00.354
2025-07-01 03:05:00.366 >>> d = Differ()
2025-07-01 03:05:00.382 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:00.390 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:00.410 >>> print(''.join(results), end="")
2025-07-01 03:05:00.418 - abcDefghiJkl
2025-07-01 03:05:00.434 + abcdefGhijkl
2025-07-01 03:05:00.458 """
2025-07-01 03:05:00.473
2025-07-01 03:05:00.486 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:00.500 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:00.509 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:00.523 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:00.538 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:00.551
2025-07-01 03:05:00.570 # search for the pair that matches best without being identical
2025-07-01 03:05:00.582 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:00.590 # on junk -- unless we have to)
2025-07-01 03:05:00.606 for j in range(blo, bhi):
2025-07-01 03:05:00.618 bj = b[j]
2025-07-01 03:05:00.626 cruncher.set_seq2(bj)
2025-07-01 03:05:00.638 for i in range(alo, ahi):
2025-07-01 03:05:00.646 ai = a[i]
2025-07-01 03:05:00.658 if ai == bj:
2025-07-01 03:05:00.666 if eqi is None:
2025-07-01 03:05:00.678 eqi, eqj = i, j
2025-07-01 03:05:00.686 continue
2025-07-01 03:05:00.696 cruncher.set_seq1(ai)
2025-07-01 03:05:00.714 # computing similarity is expensive, so use the quick
2025-07-01 03:05:00.730 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:00.746 # compares by a factor of 3.
2025-07-01 03:05:00.754 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:00.763 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:00.772 # of the computation is cached by cruncher
2025-07-01 03:05:00.778 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:00.785 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:00.791 cruncher.ratio() > best_ratio:
2025-07-01 03:05:00.797 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:00.802 if best_ratio < cutoff:
2025-07-01 03:05:00.807 # no non-identical "pretty close" pair
2025-07-01 03:05:00.814 if eqi is None:
2025-07-01 03:05:00.821 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:00.827 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:00.833 return
2025-07-01 03:05:00.839 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:00.844 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:00.850 else:
2025-07-01 03:05:00.857 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:00.864 eqi = None
2025-07-01 03:05:00.874
2025-07-01 03:05:00.879 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:00.883 # identical
2025-07-01 03:05:00.887
2025-07-01 03:05:00.892 # pump out diffs from before the synch point
2025-07-01 03:05:00.909 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:00.922
2025-07-01 03:05:00.930 # do intraline marking on the synch pair
2025-07-01 03:05:00.942 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:00.954 if eqi is None:
2025-07-01 03:05:00.966 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:00.978 atags = btags = ""
2025-07-01 03:05:00.990 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:01.002 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:01.018 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:01.030 if tag == 'replace':
2025-07-01 03:05:01.038 atags += '^' * la
2025-07-01 03:05:01.050 btags += '^' * lb
2025-07-01 03:05:01.058 elif tag == 'delete':
2025-07-01 03:05:01.070 atags += '-' * la
2025-07-01 03:05:01.086 elif tag == 'insert':
2025-07-01 03:05:01.094 btags += '+' * lb
2025-07-01 03:05:01.110 elif tag == 'equal':
2025-07-01 03:05:01.122 atags += ' ' * la
2025-07-01 03:05:01.130 btags += ' ' * lb
2025-07-01 03:05:01.146 else:
2025-07-01 03:05:01.157 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:01.166 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:01.178 else:
2025-07-01 03:05:01.186 # the synch pair is identical
2025-07-01 03:05:01.198 yield '  ' + aelt
2025-07-01 03:05:01.206
2025-07-01 03:05:01.218 # pump out diffs from after the synch point
2025-07-01 03:05:01.226 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:01.238
2025-07-01 03:05:01.250 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:01.262 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:01.272
2025-07-01 03:05:01.286 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:01.298 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:01.310 alo = 417, ahi = 1101
2025-07-01 03:05:01.325 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:01.334 blo = 417, bhi = 1101
2025-07-01 03:05:01.354
2025-07-01 03:05:01.362 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:01.374 g = []
2025-07-01 03:05:01.387 if alo < ahi:
2025-07-01 03:05:01.394 if blo < bhi:
2025-07-01 03:05:01.404 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:01.409 else:
2025-07-01 03:05:01.414 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:01.418 elif blo < bhi:
2025-07-01 03:05:01.423 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:01.427
2025-07-01 03:05:01.432 >       yield from g
2025-07-01 03:05:01.438
2025-07-01 03:05:01.446 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:01.451 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:01.456
2025-07-01 03:05:01.461 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:01.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:01.470 alo = 417, ahi = 1101
2025-07-01 03:05:01.476 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:01.480 blo = 417, bhi = 1101
2025-07-01 03:05:01.485
2025-07-01 03:05:01.489 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:01.494 r"""
2025-07-01 03:05:01.498 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:01.503 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:01.508 synch point, and intraline difference marking is done on the
2025-07-01 03:05:01.512 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:01.517
2025-07-01 03:05:01.523 Example:
2025-07-01 03:05:01.527
2025-07-01 03:05:01.532 >>> d = Differ()
2025-07-01 03:05:01.537 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:01.542 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:01.547 >>> print(''.join(results), end="")
2025-07-01 03:05:01.551 - abcDefghiJkl
2025-07-01 03:05:01.561 + abcdefGhijkl
2025-07-01 03:05:01.570 """
2025-07-01 03:05:01.575
2025-07-01 03:05:01.579 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:01.584 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:01.589 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:01.594 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:01.598 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:01.603
2025-07-01 03:05:01.608 # search for the pair that matches best without being identical
2025-07-01 03:05:01.613 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:01.618 # on junk -- unless we have to)
2025-07-01 03:05:01.623 for j in range(blo, bhi):
2025-07-01 03:05:01.627 bj = b[j]
2025-07-01 03:05:01.634 cruncher.set_seq2(bj)
2025-07-01 03:05:01.639 for i in range(alo, ahi):
2025-07-01 03:05:01.644 ai = a[i]
2025-07-01 03:05:01.650 if ai == bj:
2025-07-01 03:05:01.654 if eqi is None:
2025-07-01 03:05:01.660 eqi, eqj = i, j
2025-07-01 03:05:01.665 continue
2025-07-01 03:05:01.669 cruncher.set_seq1(ai)
2025-07-01 03:05:01.676 # computing similarity is expensive, so use the quick
2025-07-01 03:05:01.682 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:01.687 # compares by a factor of 3.
2025-07-01 03:05:01.692 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:01.697 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:01.702 # of the computation is cached by cruncher
2025-07-01 03:05:01.708 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:01.713 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:01.718 cruncher.ratio() > best_ratio:
2025-07-01 03:05:01.723 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:01.727 if best_ratio < cutoff:
2025-07-01 03:05:01.732 # no non-identical "pretty close" pair
2025-07-01 03:05:01.737 if eqi is None:
2025-07-01 03:05:01.742 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:01.747 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:01.755 return
2025-07-01 03:05:01.761 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:01.766 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:01.771 else:
2025-07-01 03:05:01.777 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:01.782 eqi = None
2025-07-01 03:05:01.788
2025-07-01 03:05:01.793 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:01.798 # identical
2025-07-01 03:05:01.803
2025-07-01 03:05:01.808 # pump out diffs from before the synch point
2025-07-01 03:05:01.813 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:01.818
2025-07-01 03:05:01.823 # do intraline marking on the synch pair
2025-07-01 03:05:01.828 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:01.833 if eqi is None:
2025-07-01 03:05:01.838 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:01.844 atags = btags = ""
2025-07-01 03:05:01.849 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:01.854 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:01.862 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:01.869 if tag == 'replace':
2025-07-01 03:05:01.874 atags += '^' * la
2025-07-01 03:05:01.880 btags += '^' * lb
2025-07-01 03:05:01.887 elif tag == 'delete':
2025-07-01 03:05:01.896 atags += '-' * la
2025-07-01 03:05:01.903 elif tag == 'insert':
2025-07-01 03:05:01.910 btags += '+' * lb
2025-07-01 03:05:01.920 elif tag == 'equal':
2025-07-01 03:05:01.933 atags += ' ' * la
2025-07-01 03:05:01.942 btags += ' ' * lb
2025-07-01 03:05:01.957 else:
2025-07-01 03:05:01.970 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:01.982 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:01.991 else:
2025-07-01 03:05:02.008 # the synch pair is identical
2025-07-01 03:05:02.025 yield '  ' + aelt
2025-07-01 03:05:02.041
2025-07-01 03:05:02.054 # pump out diffs from after the synch point
2025-07-01 03:05:02.066 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:02.081
2025-07-01 03:05:02.094 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:02.106 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:02.122
2025-07-01 03:05:02.134 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:02.149 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:02.158 alo = 418, ahi = 1101
2025-07-01 03:05:02.174 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:02.186 blo = 418, bhi = 1101
2025-07-01 03:05:02.202
2025-07-01 03:05:02.214 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:02.224 g = []
2025-07-01 03:05:02.238 if alo < ahi:
2025-07-01 03:05:02.245 if blo < bhi:
2025-07-01 03:05:02.262 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:02.274 else:
2025-07-01 03:05:02.285 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:02.298 elif blo < bhi:
2025-07-01 03:05:02.310 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:02.318
2025-07-01 03:05:02.330 >       yield from g
2025-07-01 03:05:02.338
2025-07-01 03:05:02.350 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:02.358 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:02.374
2025-07-01 03:05:02.382 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:02.394 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:02.410 alo = 418, ahi = 1101
2025-07-01 03:05:02.426 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:02.438 blo = 418, bhi = 1101
2025-07-01 03:05:02.449
2025-07-01 03:05:02.462 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:02.474 r"""
2025-07-01 03:05:02.486 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:02.498 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:02.510 synch point, and intraline difference marking is done on the
2025-07-01 03:05:02.518 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:02.530
2025-07-01 03:05:02.538 Example:
2025-07-01 03:05:02.546
2025-07-01 03:05:02.558 >>> d = Differ()
2025-07-01 03:05:02.566 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:02.582 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:02.590 >>> print(''.join(results), end="")
2025-07-01 03:05:02.606 - abcDefghiJkl
2025-07-01 03:05:02.630 + abcdefGhijkl
2025-07-01 03:05:02.654 """
2025-07-01 03:05:02.662
2025-07-01 03:05:02.674 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:02.682 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:02.694 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:02.702 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:02.714 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:02.722
2025-07-01 03:05:02.738 # search for the pair that matches best without being identical
2025-07-01 03:05:02.746 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:02.758 # on junk -- unless we have to)
2025-07-01 03:05:02.766 for j in range(blo, bhi):
2025-07-01 03:05:02.778 bj = b[j]
2025-07-01 03:05:02.786 cruncher.set_seq2(bj)
2025-07-01 03:05:02.798 for i in range(alo, ahi):
2025-07-01 03:05:02.806 ai = a[i]
2025-07-01 03:05:02.818 if ai == bj:
2025-07-01 03:05:02.826 if eqi is None:
2025-07-01 03:05:02.838 eqi, eqj = i, j
2025-07-01 03:05:02.850 continue
2025-07-01 03:05:02.858 cruncher.set_seq1(ai)
2025-07-01 03:05:02.870 # computing similarity is expensive, so use the quick
2025-07-01 03:05:02.882 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:02.890 # compares by a factor of 3.
2025-07-01 03:05:02.906 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:02.922 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:02.933 # of the computation is cached by cruncher
2025-07-01 03:05:02.946 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:02.958 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:02.966 cruncher.ratio() > best_ratio:
2025-07-01 03:05:02.978 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:02.986 if best_ratio < cutoff:
2025-07-01 03:05:02.998 # no non-identical "pretty close" pair
2025-07-01 03:05:03.014 if eqi is None:
2025-07-01 03:05:03.026 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:03.038 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:03.046 return
2025-07-01 03:05:03.056 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:03.070 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:03.086 else:
2025-07-01 03:05:03.105 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:03.114 eqi = None
2025-07-01 03:05:03.122
2025-07-01 03:05:03.132 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:03.146 # identical
2025-07-01 03:05:03.154
2025-07-01 03:05:03.163 # pump out diffs from before the synch point
2025-07-01 03:05:03.178 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:03.186
2025-07-01 03:05:03.198 # do intraline marking on the synch pair
2025-07-01 03:05:03.206 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:03.222 if eqi is None:
2025-07-01 03:05:03.232 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:03.242 atags = btags = ""
2025-07-01 03:05:03.250 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:03.270 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:03.278 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:03.286 if tag == 'replace':
2025-07-01 03:05:03.298 atags += '^' * la
2025-07-01 03:05:03.303 btags += '^' * lb
2025-07-01 03:05:03.318 elif tag == 'delete':
2025-07-01 03:05:03.330 atags += '-' * la
2025-07-01 03:05:03.338 elif tag == 'insert':
2025-07-01 03:05:03.350 btags += '+' * lb
2025-07-01 03:05:03.362 elif tag == 'equal':
2025-07-01 03:05:03.370 atags += ' ' * la
2025-07-01 03:05:03.382 btags += ' ' * lb
2025-07-01 03:05:03.390 else:
2025-07-01 03:05:03.398 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:03.410 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:03.418 else:
2025-07-01 03:05:03.430 # the synch pair is identical
2025-07-01 03:05:03.438 yield '  ' + aelt
2025-07-01 03:05:03.446
2025-07-01 03:05:03.458 # pump out diffs from after the synch point
2025-07-01 03:05:03.466 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:03.478
2025-07-01 03:05:03.491 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:03.504 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:03.514
2025-07-01 03:05:03.526 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:03.540 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:03.554 alo = 419, ahi = 1101
2025-07-01 03:05:03.566 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:03.574 blo = 419, bhi = 1101
2025-07-01 03:05:03.590
2025-07-01 03:05:03.598 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:03.606 g = []
2025-07-01 03:05:03.618 if alo < ahi:
2025-07-01 03:05:03.630 if blo < bhi:
2025-07-01 03:05:03.642 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:03.654 else:
2025-07-01 03:05:03.662 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:03.670 elif blo < bhi:
2025-07-01 03:05:03.682 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:03.689
2025-07-01 03:05:03.702 >       yield from g
2025-07-01 03:05:03.710
2025-07-01 03:05:03.722 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:03.730 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:03.742
2025-07-01 03:05:03.750 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:03.762 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:03.774 alo = 419, ahi = 1101
2025-07-01 03:05:03.781 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:03.794 blo = 419, bhi = 1101
2025-07-01 03:05:03.806
2025-07-01 03:05:03.822 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:03.832 r"""
2025-07-01 03:05:03.841 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:03.852 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:03.870 synch point, and intraline difference marking is done on the
2025-07-01 03:05:03.880 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:03.892
2025-07-01 03:05:03.910 Example:
2025-07-01 03:05:03.918
2025-07-01 03:05:03.930 >>> d = Differ()
2025-07-01 03:05:03.938 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:03.950 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:03.958 >>> print(''.join(results), end="")
2025-07-01 03:05:03.974 - abcDefghiJkl
2025-07-01 03:05:03.998 + abcdefGhijkl
2025-07-01 03:05:04.019 """
2025-07-01 03:05:04.034
2025-07-01 03:05:04.046 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:04.054 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:04.064 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:04.078 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:04.088 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:04.102
2025-07-01 03:05:04.110 # search for the pair that matches best without being identical
2025-07-01 03:05:04.126 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:04.135 # on junk -- unless we have to)
2025-07-01 03:05:04.150 for j in range(blo, bhi):
2025-07-01 03:05:04.158 bj = b[j]
2025-07-01 03:05:04.166 cruncher.set_seq2(bj)
2025-07-01 03:05:04.178 for i in range(alo, ahi):
2025-07-01 03:05:04.194 ai = a[i]
2025-07-01 03:05:04.202 if ai == bj:
2025-07-01 03:05:04.210 if eqi is None:
2025-07-01 03:05:04.218 eqi, eqj = i, j
2025-07-01 03:05:04.230 continue
2025-07-01 03:05:04.246 cruncher.set_seq1(ai)
2025-07-01 03:05:04.254 # computing similarity is expensive, so use the quick
2025-07-01 03:05:04.266 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:04.274 # compares by a factor of 3.
2025-07-01 03:05:04.286 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:04.294 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:04.306 # of the computation is cached by cruncher
2025-07-01 03:05:04.314 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:04.326 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:04.342 cruncher.ratio() > best_ratio:
2025-07-01 03:05:04.356 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:04.366 if best_ratio < cutoff:
2025-07-01 03:05:04.378 # no non-identical "pretty close" pair
2025-07-01 03:05:04.386 if eqi is None:
2025-07-01 03:05:04.398 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:04.410 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:04.418 return
2025-07-01 03:05:04.430 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:04.439 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:04.452 else:
2025-07-01 03:05:04.462 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:04.474 eqi = None
2025-07-01 03:05:04.486
2025-07-01 03:05:04.493 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:04.510 # identical
2025-07-01 03:05:04.522
2025-07-01 03:05:04.538 # pump out diffs from before the synch point
2025-07-01 03:05:04.546 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:04.554
2025-07-01 03:05:04.566 # do intraline marking on the synch pair
2025-07-01 03:05:04.573 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:04.586 if eqi is None:
2025-07-01 03:05:04.594 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:04.605 atags = btags = ""
2025-07-01 03:05:04.618 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:04.630 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:04.642 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:04.654 if tag == 'replace':
2025-07-01 03:05:04.666 atags += '^' * la
2025-07-01 03:05:04.678 btags += '^' * lb
2025-07-01 03:05:04.686 elif tag == 'delete':
2025-07-01 03:05:04.693 atags += '-' * la
2025-07-01 03:05:04.710 elif tag == 'insert':
2025-07-01 03:05:04.720 btags += '+' * lb
2025-07-01 03:05:04.734 elif tag == 'equal':
2025-07-01 03:05:04.746 atags += ' ' * la
2025-07-01 03:05:04.762 btags += ' ' * lb
2025-07-01 03:05:04.770 else:
2025-07-01 03:05:04.777 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:04.790 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:04.798 else:
2025-07-01 03:05:04.806 # the synch pair is identical
2025-07-01 03:05:04.818 yield '  ' + aelt
2025-07-01 03:05:04.826
2025-07-01 03:05:04.845 # pump out diffs from after the synch point
2025-07-01 03:05:04.863 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:04.870
2025-07-01 03:05:04.886 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:04.902 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:04.914
2025-07-01 03:05:04.922 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:04.934 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:04.946 alo = 422, ahi = 1101
2025-07-01 03:05:04.953 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:04.965 blo = 422, bhi = 1101
2025-07-01 03:05:04.974
2025-07-01 03:05:04.986 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:04.993 g = []
2025-07-01 03:05:05.006 if alo < ahi:
2025-07-01 03:05:05.018 if blo < bhi:
2025-07-01 03:05:05.026 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:05.038 else:
2025-07-01 03:05:05.050 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:05.058 elif blo < bhi:
2025-07-01 03:05:05.074 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:05.082
2025-07-01 03:05:05.094 >       yield from g
2025-07-01 03:05:05.102
2025-07-01 03:05:05.114 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:05.130 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:05.140
2025-07-01 03:05:05.154 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:05.162 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:05.170 alo = 422, ahi = 1101
2025-07-01 03:05:05.190 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:05.202 blo = 422, bhi = 1101
2025-07-01 03:05:05.214
2025-07-01 03:05:05.226 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:05.236 r"""
2025-07-01 03:05:05.254 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:05.266 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:05.274 synch point, and intraline difference marking is done on the
2025-07-01 03:05:05.286 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:05.298
2025-07-01 03:05:05.310 Example:
2025-07-01 03:05:05.318
2025-07-01 03:05:05.330 >>> d = Differ()
2025-07-01 03:05:05.342 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:05.350 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:05.362 >>> print(''.join(results), end="")
2025-07-01 03:05:05.374 - abcDefghiJkl
2025-07-01 03:05:05.393 + abcdefGhijkl
2025-07-01 03:05:05.422 """
2025-07-01 03:05:05.438
2025-07-01 03:05:05.448 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:05.462 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:05.474 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:05.482 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:05.497 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:05.514
2025-07-01 03:05:05.522 # search for the pair that matches best without being identical
2025-07-01 03:05:05.534 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:05.546 # on junk -- unless we have to)
2025-07-01 03:05:05.558 for j in range(blo, bhi):
2025-07-01 03:05:05.574 bj = b[j]
2025-07-01 03:05:05.582 cruncher.set_seq2(bj)
2025-07-01 03:05:05.594 for i in range(alo, ahi):
2025-07-01 03:05:05.602 ai = a[i]
2025-07-01 03:05:05.614 if ai == bj:
2025-07-01 03:05:05.626 if eqi is None:
2025-07-01 03:05:05.634 eqi, eqj = i, j
2025-07-01 03:05:05.646 continue
2025-07-01 03:05:05.658 cruncher.set_seq1(ai)
2025-07-01 03:05:05.674 # computing similarity is expensive, so use the quick
2025-07-01 03:05:05.684 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:05.698 # compares by a factor of 3.
2025-07-01 03:05:05.705 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:05.713 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:05.722 # of the computation is cached by cruncher
2025-07-01 03:05:05.730 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:05.737 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:05.750 cruncher.ratio() > best_ratio:
2025-07-01 03:05:05.763 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:05.771 if best_ratio < cutoff:
2025-07-01 03:05:05.778 # no non-identical "pretty close" pair
2025-07-01 03:05:05.786 if eqi is None:
2025-07-01 03:05:05.798 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:05.817 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:05.833 return
2025-07-01 03:05:05.842 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:05.851 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:05.858 else:
2025-07-01 03:05:05.873 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:05.878 eqi = None
2025-07-01 03:05:05.886
2025-07-01 03:05:05.901 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:05.913 # identical
2025-07-01 03:05:05.926
2025-07-01 03:05:05.934 # pump out diffs from before the synch point
2025-07-01 03:05:05.943 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:05.950
2025-07-01 03:05:05.961 # do intraline marking on the synch pair
2025-07-01 03:05:05.973 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:05.984 if eqi is None:
2025-07-01 03:05:05.994 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:06.005 atags = btags = ""
2025-07-01 03:05:06.014 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:06.034 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:06.046 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:06.057 if tag == 'replace':
2025-07-01 03:05:06.066 atags += '^' * la
2025-07-01 03:05:06.078 btags += '^' * lb
2025-07-01 03:05:06.089 elif tag == 'delete':
2025-07-01 03:05:06.106 atags += '-' * la
2025-07-01 03:05:06.114 elif tag == 'insert':
2025-07-01 03:05:06.130 btags += '+' * lb
2025-07-01 03:05:06.142 elif tag == 'equal':
2025-07-01 03:05:06.154 atags += ' ' * la
2025-07-01 03:05:06.166 btags += ' ' * lb
2025-07-01 03:05:06.174 else:
2025-07-01 03:05:06.190 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:06.206 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:06.218 else:
2025-07-01 03:05:06.226 # the synch pair is identical
2025-07-01 03:05:06.242 yield '  ' + aelt
2025-07-01 03:05:06.262
2025-07-01 03:05:06.270 # pump out diffs from after the synch point
2025-07-01 03:05:06.283 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:06.298
2025-07-01 03:05:06.310 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:06.321 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:06.338
2025-07-01 03:05:06.355 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:06.370 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:06.386 alo = 423, ahi = 1101
2025-07-01 03:05:06.398 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:06.410 blo = 423, bhi = 1101
2025-07-01 03:05:06.422
2025-07-01 03:05:06.430 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:06.442 g = []
2025-07-01 03:05:06.454 if alo < ahi:
2025-07-01 03:05:06.466 if blo < bhi:
2025-07-01 03:05:06.474 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:06.492 else:
2025-07-01 03:05:06.506 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:06.526 elif blo < bhi:
2025-07-01 03:05:06.542 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:06.558
2025-07-01 03:05:06.574 >       yield from g
2025-07-01 03:05:06.590
2025-07-01 03:05:06.602 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:06.610 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:06.622
2025-07-01 03:05:06.636 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:06.657 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:06.664 alo = 423, ahi = 1101
2025-07-01 03:05:06.675 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:06.686 blo = 423, bhi = 1101
2025-07-01 03:05:06.699
2025-07-01 03:05:06.721 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:06.737 r"""
2025-07-01 03:05:06.749 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:06.762 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:06.778 synch point, and intraline difference marking is done on the
2025-07-01 03:05:06.786 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:06.798
2025-07-01 03:05:06.806 Example:
2025-07-01 03:05:06.818
2025-07-01 03:05:06.826 >>> d = Differ()
2025-07-01 03:05:06.838 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:06.850 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:06.858 >>> print(''.join(results), end="")
2025-07-01 03:05:06.874 - abcDefghiJkl
2025-07-01 03:05:06.894 + abcdefGhijkl
2025-07-01 03:05:06.918 """
2025-07-01 03:05:06.930
2025-07-01 03:05:06.938 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:06.954 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:06.966 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:06.978 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:06.990 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:07.000
2025-07-01 03:05:07.013 # search for the pair that matches best without being identical
2025-07-01 03:05:07.026 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:07.034 # on junk -- unless we have to)
2025-07-01 03:05:07.046 for j in range(blo, bhi):
2025-07-01 03:05:07.056 bj = b[j]
2025-07-01 03:05:07.070 cruncher.set_seq2(bj)
2025-07-01 03:05:07.082 for i in range(alo, ahi):
2025-07-01 03:05:07.090 ai = a[i]
2025-07-01 03:05:07.102 if ai == bj:
2025-07-01 03:05:07.110 if eqi is None:
2025-07-01 03:05:07.122 eqi, eqj = i, j
2025-07-01 03:05:07.130 continue
2025-07-01 03:05:07.142 cruncher.set_seq1(ai)
2025-07-01 03:05:07.149 # computing similarity is expensive, so use the quick
2025-07-01 03:05:07.165 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:07.182 # compares by a factor of 3.
2025-07-01 03:05:07.194 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:07.206 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:07.222 # of the computation is cached by cruncher
2025-07-01 03:05:07.234 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:07.242 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:07.262 cruncher.ratio() > best_ratio:
2025-07-01 03:05:07.270 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:07.278 if best_ratio < cutoff:
2025-07-01 03:05:07.286 # no non-identical "pretty close" pair
2025-07-01 03:05:07.302 if eqi is None:
2025-07-01 03:05:07.314 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:07.326 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:07.338 return
2025-07-01 03:05:07.346 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:07.358 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:07.370 else:
2025-07-01 03:05:07.377 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:07.390 eqi = None
2025-07-01 03:05:07.398
2025-07-01 03:05:07.410 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:07.422 # identical
2025-07-01 03:05:07.430
2025-07-01 03:05:07.442 # pump out diffs from before the synch point
2025-07-01 03:05:07.452 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:07.466
2025-07-01 03:05:07.478 # do intraline marking on the synch pair
2025-07-01 03:05:07.483 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:07.494 if eqi is None:
2025-07-01 03:05:07.507 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:07.522 atags = btags = ""
2025-07-01 03:05:07.530 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:07.542 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:07.554 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:07.562 if tag == 'replace':
2025-07-01 03:05:07.578 atags += '^' * la
2025-07-01 03:05:07.586 btags += '^' * lb
2025-07-01 03:05:07.598 elif tag == 'delete':
2025-07-01 03:05:07.606 atags += '-' * la
2025-07-01 03:05:07.618 elif tag == 'insert':
2025-07-01 03:05:07.638 btags += '+' * lb
2025-07-01 03:05:07.646 elif tag == 'equal':
2025-07-01 03:05:07.654 atags += ' ' * la
2025-07-01 03:05:07.668 btags += ' ' * lb
2025-07-01 03:05:07.679 else:
2025-07-01 03:05:07.688 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:07.706 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:07.714 else:
2025-07-01 03:05:07.726 # the synch pair is identical
2025-07-01 03:05:07.742 yield '  ' + aelt
2025-07-01 03:05:07.759
2025-07-01 03:05:07.774 # pump out diffs from after the synch point
2025-07-01 03:05:07.786 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:07.797
2025-07-01 03:05:07.814 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:07.821 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:07.834
2025-07-01 03:05:07.846 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:07.858 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:07.865 alo = 424, ahi = 1101
2025-07-01 03:05:07.882 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:07.890 blo = 424, bhi = 1101
2025-07-01 03:05:07.902
2025-07-01 03:05:07.910 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:07.918 g = []
2025-07-01 03:05:07.934 if alo < ahi:
2025-07-01 03:05:07.941 if blo < bhi:
2025-07-01 03:05:07.958 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:07.966 else:
2025-07-01 03:05:07.982 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:07.990 elif blo < bhi:
2025-07-01 03:05:08.003 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:08.018
2025-07-01 03:05:08.030 >       yield from g
2025-07-01 03:05:08.042
2025-07-01 03:05:08.050 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:08.062 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:08.074
2025-07-01 03:05:08.082 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:08.093 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:08.106 alo = 424, ahi = 1101
2025-07-01 03:05:08.118 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:08.130 blo = 424, bhi = 1101
2025-07-01 03:05:08.146
2025-07-01 03:05:08.164 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:08.178 r"""
2025-07-01 03:05:08.194 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:08.202 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:08.218 synch point, and intraline difference marking is done on the
2025-07-01 03:05:08.230 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:08.238
2025-07-01 03:05:08.254 Example:
2025-07-01 03:05:08.269
2025-07-01 03:05:08.278 >>> d = Differ()
2025-07-01 03:05:08.294 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:08.306 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:08.326 >>> print(''.join(results), end="")
2025-07-01 03:05:08.334 - abcDefghiJkl
2025-07-01 03:05:08.362 + abcdefGhijkl
2025-07-01 03:05:08.382 """
2025-07-01 03:05:08.390
2025-07-01 03:05:08.406 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:08.418 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:08.430 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:08.438 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:08.450 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:08.462
2025-07-01 03:05:08.470 # search for the pair that matches best without being identical
2025-07-01 03:05:08.482 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:08.494 # on junk -- unless we have to)
2025-07-01 03:05:08.506 for j in range(blo, bhi):
2025-07-01 03:05:08.514 bj = b[j]
2025-07-01 03:05:08.526 cruncher.set_seq2(bj)
2025-07-01 03:05:08.538 for i in range(alo, ahi):
2025-07-01 03:05:08.550 ai = a[i]
2025-07-01 03:05:08.562 if ai == bj:
2025-07-01 03:05:08.570 if eqi is None:
2025-07-01 03:05:08.582 eqi, eqj = i, j
2025-07-01 03:05:08.594 continue
2025-07-01 03:05:08.602 cruncher.set_seq1(ai)
2025-07-01 03:05:08.614 # computing similarity is expensive, so use the quick
2025-07-01 03:05:08.626 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:08.638 # compares by a factor of 3.
2025-07-01 03:05:08.646 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:08.658 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:08.674 # of the computation is cached by cruncher
2025-07-01 03:05:08.682 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:08.694 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:08.706 cruncher.ratio() > best_ratio:
2025-07-01 03:05:08.714 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:08.722 if best_ratio < cutoff:
2025-07-01 03:05:08.731 # no non-identical "pretty close" pair
2025-07-01 03:05:08.748 if eqi is None:
2025-07-01 03:05:08.766 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:08.779 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:08.785 return
2025-07-01 03:05:08.798 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:08.810 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:08.821 else:
2025-07-01 03:05:08.835 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:08.850 eqi = None
2025-07-01 03:05:08.862
2025-07-01 03:05:08.879 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:08.894 # identical
2025-07-01 03:05:08.902
2025-07-01 03:05:08.914 # pump out diffs from before the synch point
2025-07-01 03:05:08.933 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:08.950
2025-07-01 03:05:08.963 # do intraline marking on the synch pair
2025-07-01 03:05:08.971 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:08.983 if eqi is None:
2025-07-01 03:05:09.002 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:09.020 atags = btags = ""
2025-07-01 03:05:09.033 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:09.041 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:09.050 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:09.065 if tag == 'replace':
2025-07-01 03:05:09.080 atags += '^' * la
2025-07-01 03:05:09.094 btags += '^' * lb
2025-07-01 03:05:09.102 elif tag == 'delete':
2025-07-01 03:05:09.112 atags += '-' * la
2025-07-01 03:05:09.125 elif tag == 'insert':
2025-07-01 03:05:09.137 btags += '+' * lb
2025-07-01 03:05:09.144 elif tag == 'equal':
2025-07-01 03:05:09.154 atags += ' ' * la
2025-07-01 03:05:09.164 btags += ' ' * lb
2025-07-01 03:05:09.178 else:
2025-07-01 03:05:09.191 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:09.199 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:09.214 else:
2025-07-01 03:05:09.222 # the synch pair is identical
2025-07-01 03:05:09.231 yield '  ' + aelt
2025-07-01 03:05:09.241
2025-07-01 03:05:09.253 # pump out diffs from after the synch point
2025-07-01 03:05:09.265 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:09.278
2025-07-01 03:05:09.290 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:09.300 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:09.314
2025-07-01 03:05:09.330 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:09.338 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:09.350 alo = 425, ahi = 1101
2025-07-01 03:05:09.370 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:09.378 blo = 425, bhi = 1101
2025-07-01 03:05:09.390
2025-07-01 03:05:09.398 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:09.410 g = []
2025-07-01 03:05:09.418 if alo < ahi:
2025-07-01 03:05:09.430 if blo < bhi:
2025-07-01 03:05:09.438 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:09.450 else:
2025-07-01 03:05:09.458 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:09.470 elif blo < bhi:
2025-07-01 03:05:09.482 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:09.490
2025-07-01 03:05:09.502 >       yield from g
2025-07-01 03:05:09.510
2025-07-01 03:05:09.522 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:09.530 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:09.542
2025-07-01 03:05:09.548 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:09.566 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:09.576 alo = 425, ahi = 1101
2025-07-01 03:05:09.588 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:09.602 blo = 425, bhi = 1101
2025-07-01 03:05:09.618
2025-07-01 03:05:09.625 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:09.634 r"""
2025-07-01 03:05:09.642 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:09.653 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:09.666 synch point, and intraline difference marking is done on the
2025-07-01 03:05:09.674 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:09.684
2025-07-01 03:05:09.702 Example:
2025-07-01 03:05:09.714
2025-07-01 03:05:09.726 >>> d = Differ()
2025-07-01 03:05:09.732 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:09.742 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:09.754 >>> print(''.join(results), end="")
2025-07-01 03:05:09.762 - abcDefghiJkl
2025-07-01 03:05:09.784 + abcdefGhijkl
2025-07-01 03:05:09.814 """
2025-07-01 03:05:09.822
2025-07-01 03:05:09.830 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:09.842 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:09.854 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:09.862 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:09.874 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:09.886
2025-07-01 03:05:09.892 # search for the pair that matches best without being identical
2025-07-01 03:05:09.906 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:09.913 # on junk -- unless we have to)
2025-07-01 03:05:09.925 for j in range(blo, bhi):
2025-07-01 03:05:09.934 bj = b[j]
2025-07-01 03:05:09.946 cruncher.set_seq2(bj)
2025-07-01 03:05:09.952 for i in range(alo, ahi):
2025-07-01 03:05:09.966 ai = a[i]
2025-07-01 03:05:09.978 if ai == bj:
2025-07-01 03:05:09.990 if eqi is None:
2025-07-01 03:05:10.001 eqi, eqj = i, j
2025-07-01 03:05:10.014 continue
2025-07-01 03:05:10.022 cruncher.set_seq1(ai)
2025-07-01 03:05:10.029 # computing similarity is expensive, so use the quick
2025-07-01 03:05:10.038 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:10.050 # compares by a factor of 3.
2025-07-01 03:05:10.066 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:10.078 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:10.090 # of the computation is cached by cruncher
2025-07-01 03:05:10.102 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:10.114 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:10.121 cruncher.ratio() > best_ratio:
2025-07-01 03:05:10.134 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:10.141 if best_ratio < cutoff:
2025-07-01 03:05:10.147 # no non-identical "pretty close" pair
2025-07-01 03:05:10.161 if eqi is None:
2025-07-01 03:05:10.174 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:10.182 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:10.194 return
2025-07-01 03:05:10.201 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:10.209 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:10.224 else:
2025-07-01 03:05:10.234 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:10.246 eqi = None
2025-07-01 03:05:10.254
2025-07-01 03:05:10.266 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:10.278 # identical
2025-07-01 03:05:10.290
2025-07-01 03:05:10.301 # pump out diffs from before the synch point
2025-07-01 03:05:10.310 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:10.318
2025-07-01 03:05:10.330 # do intraline marking on the synch pair
2025-07-01 03:05:10.338 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:10.348 if eqi is None:
2025-07-01 03:05:10.365 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:10.374 atags = btags = ""
2025-07-01 03:05:10.381 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:10.392 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:10.406 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:10.420 if tag == 'replace':
2025-07-01 03:05:10.430 atags += '^' * la
2025-07-01 03:05:10.442 btags += '^' * lb
2025-07-01 03:05:10.454 elif tag == 'delete':
2025-07-01 03:05:10.465 atags += '-' * la
2025-07-01 03:05:10.478 elif tag == 'insert':
2025-07-01 03:05:10.488 btags += '+' * lb
2025-07-01 03:05:10.500 elif tag == 'equal':
2025-07-01 03:05:10.516 atags += ' ' * la
2025-07-01 03:05:10.524 btags += ' ' * lb
2025-07-01 03:05:10.538 else:
2025-07-01 03:05:10.550 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:10.558 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:10.570 else:
2025-07-01 03:05:10.578 # the synch pair is identical
2025-07-01 03:05:10.590 yield '  ' + aelt
2025-07-01 03:05:10.602
2025-07-01 03:05:10.609 # pump out diffs from after the synch point
2025-07-01 03:05:10.622 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:10.630
2025-07-01 03:05:10.650 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:10.667 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:10.678
2025-07-01 03:05:10.699 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:10.713 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:10.722 alo = 426, ahi = 1101
2025-07-01 03:05:10.737 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:10.746 blo = 426, bhi = 1101
2025-07-01 03:05:10.761
2025-07-01 03:05:10.771 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:10.780 g = []
2025-07-01 03:05:10.791 if alo < ahi:
2025-07-01 03:05:10.804 if blo < bhi:
2025-07-01 03:05:10.822 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:10.834 else:
2025-07-01 03:05:10.842 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:10.853 elif blo < bhi:
2025-07-01 03:05:10.870 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:10.879
2025-07-01 03:05:10.894 >       yield from g
2025-07-01 03:05:10.900
2025-07-01 03:05:10.914 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:10.921 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:10.925
2025-07-01 03:05:10.930 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:10.942 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:10.949 alo = 426, ahi = 1101
2025-07-01 03:05:10.964 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:10.974 blo = 426, bhi = 1101
2025-07-01 03:05:10.986
2025-07-01 03:05:10.997 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:11.006 r"""
2025-07-01 03:05:11.018 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:11.030 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:11.042 synch point, and intraline difference marking is done on the
2025-07-01 03:05:11.057 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:11.062
2025-07-01 03:05:11.074 Example:
2025-07-01 03:05:11.085
2025-07-01 03:05:11.098 >>> d = Differ()
2025-07-01 03:05:11.110 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:11.122 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:11.134 >>> print(''.join(results), end="")
2025-07-01 03:05:11.142 - abcDefghiJkl
2025-07-01 03:05:11.170 + abcdefGhijkl
2025-07-01 03:05:11.190 """
2025-07-01 03:05:11.198
2025-07-01 03:05:11.206 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:11.218 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:11.226 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:11.242 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:11.250 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:11.262
2025-07-01 03:05:11.278 # search for the pair that matches best without being identical
2025-07-01 03:05:11.287 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:11.301 # on junk -- unless we have to)
2025-07-01 03:05:11.310 for j in range(blo, bhi):
2025-07-01 03:05:11.317 bj = b[j]
2025-07-01 03:05:11.323 cruncher.set_seq2(bj)
2025-07-01 03:05:11.338 for i in range(alo, ahi):
2025-07-01 03:05:11.354 ai = a[i]
2025-07-01 03:05:11.364 if ai == bj:
2025-07-01 03:05:11.374 if eqi is None:
2025-07-01 03:05:11.382 eqi, eqj = i, j
2025-07-01 03:05:11.395 continue
2025-07-01 03:05:11.402 cruncher.set_seq1(ai)
2025-07-01 03:05:11.414 # computing similarity is expensive, so use the quick
2025-07-01 03:05:11.426 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:11.434 # compares by a factor of 3.
2025-07-01 03:05:11.446 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:11.454 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:11.465 # of the computation is cached by cruncher
2025-07-01 03:05:11.478 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:11.486 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:11.498 cruncher.ratio() > best_ratio:
2025-07-01 03:05:11.506 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:11.518 if best_ratio < cutoff:
2025-07-01 03:05:11.526 # no non-identical "pretty close" pair
2025-07-01 03:05:11.538 if eqi is None:
2025-07-01 03:05:11.546 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:11.562 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:11.570 return
2025-07-01 03:05:11.586 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:11.597 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:11.606 else:
2025-07-01 03:05:11.622 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:11.635 eqi = None
2025-07-01 03:05:11.643
2025-07-01 03:05:11.654 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:11.662 # identical
2025-07-01 03:05:11.673
2025-07-01 03:05:11.686 # pump out diffs from before the synch point
2025-07-01 03:05:11.698 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:11.710
2025-07-01 03:05:11.718 # do intraline marking on the synch pair
2025-07-01 03:05:11.734 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:11.741 if eqi is None:
2025-07-01 03:05:11.758 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:11.766 atags = btags = ""
2025-07-01 03:05:11.778 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:11.786 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:11.802 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:11.810 if tag == 'replace':
2025-07-01 03:05:11.826 atags += '^' * la
2025-07-01 03:05:11.834 btags += '^' * lb
2025-07-01 03:05:11.854 elif tag == 'delete':
2025-07-01 03:05:11.862 atags += '-' * la
2025-07-01 03:05:11.873 elif tag == 'insert':
2025-07-01 03:05:11.877 btags += '+' * lb
2025-07-01 03:05:11.882 elif tag == 'equal':
2025-07-01 03:05:11.886 atags += ' ' * la
2025-07-01 03:05:11.890 btags += ' ' * lb
2025-07-01 03:05:11.895 else:
2025-07-01 03:05:11.899 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:11.906 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:11.921 else:
2025-07-01 03:05:11.934 # the synch pair is identical
2025-07-01 03:05:11.946 yield '  ' + aelt
2025-07-01 03:05:11.954
2025-07-01 03:05:11.962 # pump out diffs from after the synch point
2025-07-01 03:05:11.978 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:11.990
2025-07-01 03:05:11.998 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:12.014 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:12.022
2025-07-01 03:05:12.038 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:12.045 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:12.050 alo = 427, ahi = 1101
2025-07-01 03:05:12.057 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:12.063 blo = 427, bhi = 1101
2025-07-01 03:05:12.067
2025-07-01 03:05:12.072 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:12.077 g = []
2025-07-01 03:05:12.082 if alo < ahi:
2025-07-01 03:05:12.086 if blo < bhi:
2025-07-01 03:05:12.091 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:12.095 else:
2025-07-01 03:05:12.101 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:12.105 elif blo < bhi:
2025-07-01 03:05:12.110 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:12.115
2025-07-01 03:05:12.120 >       yield from g
2025-07-01 03:05:12.125
2025-07-01 03:05:12.131 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:12.136 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:12.141
2025-07-01 03:05:12.156 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:12.170 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:12.186 alo = 427, ahi = 1101
2025-07-01 03:05:12.194 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:12.210 blo = 427, bhi = 1101
2025-07-01 03:05:12.218
2025-07-01 03:05:12.230 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:12.242 r"""
2025-07-01 03:05:12.250 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:12.262 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:12.278 synch point, and intraline difference marking is done on the
2025-07-01 03:05:12.291 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:12.302
2025-07-01 03:05:12.322 Example:
2025-07-01 03:05:12.330
2025-07-01 03:05:12.345 >>> d = Differ()
2025-07-01 03:05:12.356 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:12.366 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:12.382 >>> print(''.join(results), end="")
2025-07-01 03:05:12.398 - abcDefghiJkl
2025-07-01 03:05:12.418 + abcdefGhijkl
2025-07-01 03:05:12.442 """
2025-07-01 03:05:12.454
2025-07-01 03:05:12.466 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:12.478 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:12.490 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:12.498 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:12.510 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:12.526
2025-07-01 03:05:12.534 # search for the pair that matches best without being identical
2025-07-01 03:05:12.550 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:12.558 # on junk -- unless we have to)
2025-07-01 03:05:12.574 for j in range(blo, bhi):
2025-07-01 03:05:12.582 bj = b[j]
2025-07-01 03:05:12.594 cruncher.set_seq2(bj)
2025-07-01 03:05:12.606 for i in range(alo, ahi):
2025-07-01 03:05:12.617 ai = a[i]
2025-07-01 03:05:12.637 if ai == bj:
2025-07-01 03:05:12.650 if eqi is None:
2025-07-01 03:05:12.664 eqi, eqj = i, j
2025-07-01 03:05:12.672 continue
2025-07-01 03:05:12.682 cruncher.set_seq1(ai)
2025-07-01 03:05:12.697 # computing similarity is expensive, so use the quick
2025-07-01 03:05:12.710 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:12.722 # compares by a factor of 3.
2025-07-01 03:05:12.728 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:12.742 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:12.754 # of the computation is cached by cruncher
2025-07-01 03:05:12.762 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:12.774 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:12.780 cruncher.ratio() > best_ratio:
2025-07-01 03:05:12.785 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:12.790 if best_ratio < cutoff:
2025-07-01 03:05:12.794 # no non-identical "pretty close" pair
2025-07-01 03:05:12.800 if eqi is None:
2025-07-01 03:05:12.805 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:12.809 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:12.814 return
2025-07-01 03:05:12.819 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:12.824 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:12.829 else:
2025-07-01 03:05:12.833 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:12.838 eqi = None
2025-07-01 03:05:12.842
2025-07-01 03:05:12.847 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:12.852 # identical
2025-07-01 03:05:12.856
2025-07-01 03:05:12.860 # pump out diffs from before the synch point
2025-07-01 03:05:12.865 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:12.869
2025-07-01 03:05:12.874 # do intraline marking on the synch pair
2025-07-01 03:05:12.878 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:12.884 if eqi is None:
2025-07-01 03:05:12.888 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:12.893 atags = btags = ""
2025-07-01 03:05:12.897 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:12.902 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:12.906 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:12.911 if tag == 'replace':
2025-07-01 03:05:12.918 atags += '^' * la
2025-07-01 03:05:12.938 btags += '^' * lb
2025-07-01 03:05:12.950 elif tag == 'delete':
2025-07-01 03:05:12.962 atags += '-' * la
2025-07-01 03:05:12.970 elif tag == 'insert':
2025-07-01 03:05:12.981 btags += '+' * lb
2025-07-01 03:05:12.994 elif tag == 'equal':
2025-07-01 03:05:13.006 atags += ' ' * la
2025-07-01 03:05:13.018 btags += ' ' * lb
2025-07-01 03:05:13.030 else:
2025-07-01 03:05:13.041 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:13.053 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:13.066 else:
2025-07-01 03:05:13.078 # the synch pair is identical
2025-07-01 03:05:13.092 yield '  ' + aelt
2025-07-01 03:05:13.102
2025-07-01 03:05:13.114 # pump out diffs from after the synch point
2025-07-01 03:05:13.126 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:13.138
2025-07-01 03:05:13.146 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:13.158 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:13.170
2025-07-01 03:05:13.178 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:13.190 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:13.206 alo = 428, ahi = 1101
2025-07-01 03:05:13.218 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:13.229 blo = 428, bhi = 1101
2025-07-01 03:05:13.242
2025-07-01 03:05:13.251 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:13.259 g = []
2025-07-01 03:05:13.274 if alo < ahi:
2025-07-01 03:05:13.290 if blo < bhi:
2025-07-01 03:05:13.304 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:13.316 else:
2025-07-01 03:05:13.326 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:13.338 elif blo < bhi:
2025-07-01 03:05:13.346 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:13.358
2025-07-01 03:05:13.370 >       yield from g
2025-07-01 03:05:13.382
2025-07-01 03:05:13.394 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:13.406 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:13.422
2025-07-01 03:05:13.430 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:13.446 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:13.458 alo = 428, ahi = 1101
2025-07-01 03:05:13.473 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:13.481 blo = 428, bhi = 1101
2025-07-01 03:05:13.494
2025-07-01 03:05:13.506 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:13.518 r"""
2025-07-01 03:05:13.535 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:13.546 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:13.558 synch point, and intraline difference marking is done on the
2025-07-01 03:05:13.574 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:13.585
2025-07-01 03:05:13.594 Example:
2025-07-01 03:05:13.606
2025-07-01 03:05:13.615 >>> d = Differ()
2025-07-01 03:05:13.629 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:13.639 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:13.650 >>> print(''.join(results), end="")
2025-07-01 03:05:13.662 - abcDefghiJkl
2025-07-01 03:05:13.684 + abcdefGhijkl
2025-07-01 03:05:13.706 """
2025-07-01 03:05:13.713
2025-07-01 03:05:13.726 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:13.738 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:13.746 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:13.762 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:13.770 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:13.781
2025-07-01 03:05:13.790 # search for the pair that matches best without being identical
2025-07-01 03:05:13.802 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:13.812 # on junk -- unless we have to)
2025-07-01 03:05:13.826 for j in range(blo, bhi):
2025-07-01 03:05:13.834 bj = b[j]
2025-07-01 03:05:13.846 cruncher.set_seq2(bj)
2025-07-01 03:05:13.854 for i in range(alo, ahi):
2025-07-01 03:05:13.864 ai = a[i]
2025-07-01 03:05:13.876 if ai == bj:
2025-07-01 03:05:13.882 if eqi is None:
2025-07-01 03:05:13.887 eqi, eqj = i, j
2025-07-01 03:05:13.893 continue
2025-07-01 03:05:13.899 cruncher.set_seq1(ai)
2025-07-01 03:05:13.906 # computing similarity is expensive, so use the quick
2025-07-01 03:05:13.911 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:13.924 # compares by a factor of 3.
2025-07-01 03:05:13.931 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:13.942 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:13.962 # of the computation is cached by cruncher
2025-07-01 03:05:13.974 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:13.982 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:13.994 cruncher.ratio() > best_ratio:
2025-07-01 03:05:14.006 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:14.014 if best_ratio < cutoff:
2025-07-01 03:05:14.030 # no non-identical "pretty close" pair
2025-07-01 03:05:14.038 if eqi is None:
2025-07-01 03:05:14.050 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:14.062 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:14.070 return
2025-07-01 03:05:14.082 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:14.098 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:14.110 else:
2025-07-01 03:05:14.126 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:14.142 eqi = None
2025-07-01 03:05:14.158
2025-07-01 03:05:14.166 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:14.178 # identical
2025-07-01 03:05:14.186
2025-07-01 03:05:14.203 # pump out diffs from before the synch point
2025-07-01 03:05:14.211 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:14.218
2025-07-01 03:05:14.229 # do intraline marking on the synch pair
2025-07-01 03:05:14.245 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:14.262 if eqi is None:
2025-07-01 03:05:14.274 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:14.284 atags = btags = ""
2025-07-01 03:05:14.300 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:14.310 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:14.322 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:14.333 if tag == 'replace':
2025-07-01 03:05:14.350 atags += '^' * la
2025-07-01 03:05:14.360 btags += '^' * lb
2025-07-01 03:05:14.375 elif tag == 'delete':
2025-07-01 03:05:14.393 atags += '-' * la
2025-07-01 03:05:14.409 elif tag == 'insert':
2025-07-01 03:05:14.425 btags += '+' * lb
2025-07-01 03:05:14.438 elif tag == 'equal':
2025-07-01 03:05:14.452 atags += ' ' * la
2025-07-01 03:05:14.463 btags += ' ' * lb
2025-07-01 03:05:14.468 else:
2025-07-01 03:05:14.473 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:14.482 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:14.494 else:
2025-07-01 03:05:14.502 # the synch pair is identical
2025-07-01 03:05:14.514 yield '  ' + aelt
2025-07-01 03:05:14.526
2025-07-01 03:05:14.538 # pump out diffs from after the synch point
2025-07-01 03:05:14.546 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:14.557
2025-07-01 03:05:14.566 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:14.578 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:14.582
2025-07-01 03:05:14.587 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:14.594 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:14.610 alo = 429, ahi = 1101
2025-07-01 03:05:14.622 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:14.638 blo = 429, bhi = 1101
2025-07-01 03:05:14.645
2025-07-01 03:05:14.654 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:14.665 g = []
2025-07-01 03:05:14.678 if alo < ahi:
2025-07-01 03:05:14.694 if blo < bhi:
2025-07-01 03:05:14.702 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:14.716 else:
2025-07-01 03:05:14.730 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:14.742 elif blo < bhi:
2025-07-01 03:05:14.750 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:14.762
2025-07-01 03:05:14.770 >       yield from g
2025-07-01 03:05:14.782
2025-07-01 03:05:14.790 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:14.802 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:14.810
2025-07-01 03:05:14.822 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:14.834 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:14.846 alo = 429, ahi = 1101
2025-07-01 03:05:14.853 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:14.865 blo = 429, bhi = 1101
2025-07-01 03:05:14.877
2025-07-01 03:05:14.885 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:14.901 r"""
2025-07-01 03:05:14.914 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:14.922 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:14.930 synch point, and intraline difference marking is done on the
2025-07-01 03:05:14.946 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:14.954
2025-07-01 03:05:14.966 Example:
2025-07-01 03:05:14.982
2025-07-01 03:05:14.990 >>> d = Differ()
2025-07-01 03:05:15.002 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:15.009 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:15.022 >>> print(''.join(results), end="")
2025-07-01 03:05:15.030 - abcDefghiJkl
2025-07-01 03:05:15.050 + abcdefGhijkl
2025-07-01 03:05:15.069 """
2025-07-01 03:05:15.084
2025-07-01 03:05:15.094 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:15.106 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:15.113 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:15.121 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:15.134 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:15.146
2025-07-01 03:05:15.151 # search for the pair that matches best without being identical
2025-07-01 03:05:15.156 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:15.162 # on junk -- unless we have to)
2025-07-01 03:05:15.168 for j in range(blo, bhi):
2025-07-01 03:05:15.173 bj = b[j]
2025-07-01 03:05:15.179 cruncher.set_seq2(bj)
2025-07-01 03:05:15.183 for i in range(alo, ahi):
2025-07-01 03:05:15.192 ai = a[i]
2025-07-01 03:05:15.197 if ai == bj:
2025-07-01 03:05:15.201 if eqi is None:
2025-07-01 03:05:15.212 eqi, eqj = i, j
2025-07-01 03:05:15.230 continue
2025-07-01 03:05:15.242 cruncher.set_seq1(ai)
2025-07-01 03:05:15.257 # computing similarity is expensive, so use the quick
2025-07-01 03:05:15.269 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:15.282 # compares by a factor of 3.
2025-07-01 03:05:15.290 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:15.302 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:15.314 # of the computation is cached by cruncher
2025-07-01 03:05:15.322 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:15.334 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:15.342 cruncher.ratio() > best_ratio:
2025-07-01 03:05:15.354 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:15.362 if best_ratio < cutoff:
2025-07-01 03:05:15.374 # no non-identical "pretty close" pair
2025-07-01 03:05:15.382 if eqi is None:
2025-07-01 03:05:15.390 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:15.397 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:15.405 return
2025-07-01 03:05:15.418 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:15.433 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:15.442 else:
2025-07-01 03:05:15.462 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:15.467 eqi = None
2025-07-01 03:05:15.482
2025-07-01 03:05:15.489 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:15.502 # identical
2025-07-01 03:05:15.510
2025-07-01 03:05:15.518 # pump out diffs from before the synch point
2025-07-01 03:05:15.530 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:15.543
2025-07-01 03:05:15.552 # do intraline marking on the synch pair
2025-07-01 03:05:15.570 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:15.582 if eqi is None:
2025-07-01 03:05:15.590 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:15.598 atags = btags = ""
2025-07-01 03:05:15.610 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:15.618 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:15.630 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:15.636 if tag == 'replace':
2025-07-01 03:05:15.642 atags += '^' * la
2025-07-01 03:05:15.647 btags += '^' * lb
2025-07-01 03:05:15.652 elif tag == 'delete':
2025-07-01 03:05:15.657 atags += '-' * la
2025-07-01 03:05:15.662 elif tag == 'insert':
2025-07-01 03:05:15.667 btags += '+' * lb
2025-07-01 03:05:15.672 elif tag == 'equal':
2025-07-01 03:05:15.676 atags += ' ' * la
2025-07-01 03:05:15.681 btags += ' ' * lb
2025-07-01 03:05:15.685 else:
2025-07-01 03:05:15.690 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:15.694 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:15.699 else:
2025-07-01 03:05:15.703 # the synch pair is identical
2025-07-01 03:05:15.708 yield '  ' + aelt
2025-07-01 03:05:15.712
2025-07-01 03:05:15.717 # pump out diffs from after the synch point
2025-07-01 03:05:15.723 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:15.728
2025-07-01 03:05:15.732 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:15.737 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:15.741
2025-07-01 03:05:15.746 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:15.752 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:15.756 alo = 430, ahi = 1101
2025-07-01 03:05:15.762 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:15.766 blo = 430, bhi = 1101
2025-07-01 03:05:15.770
2025-07-01 03:05:15.775 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:15.779 g = []
2025-07-01 03:05:15.784 if alo < ahi:
2025-07-01 03:05:15.788 if blo < bhi:
2025-07-01 03:05:15.793 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:15.797 else:
2025-07-01 03:05:15.802 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:15.806 elif blo < bhi:
2025-07-01 03:05:15.811 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:15.815
2025-07-01 03:05:15.820 >       yield from g
2025-07-01 03:05:15.824
2025-07-01 03:05:15.829 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:15.833 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:15.837
2025-07-01 03:05:15.842 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:15.847 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:15.851 alo = 430, ahi = 1101
2025-07-01 03:05:15.856 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:15.861 blo = 430, bhi = 1101
2025-07-01 03:05:15.865
2025-07-01 03:05:15.870 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:15.874 r"""
2025-07-01 03:05:15.878 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:15.883 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:15.888 synch point, and intraline difference marking is done on the
2025-07-01 03:05:15.892 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:15.897
2025-07-01 03:05:15.902 Example:
2025-07-01 03:05:15.906
2025-07-01 03:05:15.915 >>> d = Differ()
2025-07-01 03:05:15.920 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:15.925 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:15.930 >>> print(''.join(results), end="")
2025-07-01 03:05:15.935 - abcDefghiJkl
2025-07-01 03:05:15.948 + abcdefGhijkl
2025-07-01 03:05:15.978 """
2025-07-01 03:05:15.982
2025-07-01 03:05:15.987 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:15.992 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:15.996 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:16.001 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:16.006 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:16.011
2025-07-01 03:05:16.016 # search for the pair that matches best without being identical
2025-07-01 03:05:16.020 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:16.025 # on junk -- unless we have to)
2025-07-01 03:05:16.029 for j in range(blo, bhi):
2025-07-01 03:05:16.034 bj = b[j]
2025-07-01 03:05:16.040 cruncher.set_seq2(bj)
2025-07-01 03:05:16.045 for i in range(alo, ahi):
2025-07-01 03:05:16.049 ai = a[i]
2025-07-01 03:05:16.054 if ai == bj:
2025-07-01 03:05:16.058 if eqi is None:
2025-07-01 03:05:16.063 eqi, eqj = i, j
2025-07-01 03:05:16.067 continue
2025-07-01 03:05:16.072 cruncher.set_seq1(ai)
2025-07-01 03:05:16.076 # computing similarity is expensive, so use the quick
2025-07-01 03:05:16.081 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:16.086 # compares by a factor of 3.
2025-07-01 03:05:16.090 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:16.095 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:16.099 # of the computation is cached by cruncher
2025-07-01 03:05:16.104 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:16.109 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:16.113 cruncher.ratio() > best_ratio:
2025-07-01 03:05:16.117 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:16.122 if best_ratio < cutoff:
2025-07-01 03:05:16.126 # no non-identical "pretty close" pair
2025-07-01 03:05:16.130 if eqi is None:
2025-07-01 03:05:16.135 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:16.139 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:16.144 return
2025-07-01 03:05:16.158 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:16.166 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:16.182 else:
2025-07-01 03:05:16.194 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:16.201 eqi = None
2025-07-01 03:05:16.214
2025-07-01 03:05:16.226 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:16.238 # identical
2025-07-01 03:05:16.246
2025-07-01 03:05:16.262 # pump out diffs from before the synch point
2025-07-01 03:05:16.274 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:16.283
2025-07-01 03:05:16.298 # do intraline marking on the synch pair
2025-07-01 03:05:16.310 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:16.317 if eqi is None:
2025-07-01 03:05:16.330 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:16.338 atags = btags = ""
2025-07-01 03:05:16.350 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:16.365 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:16.375 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:16.384 if tag == 'replace':
2025-07-01 03:05:16.389 atags += '^' * la
2025-07-01 03:05:16.398 btags += '^' * lb
2025-07-01 03:05:16.413 elif tag == 'delete':
2025-07-01 03:05:16.422 atags += '-' * la
2025-07-01 03:05:16.437 elif tag == 'insert':
2025-07-01 03:05:16.447 btags += '+' * lb
2025-07-01 03:05:16.465 elif tag == 'equal':
2025-07-01 03:05:16.479 atags += ' ' * la
2025-07-01 03:05:16.494 btags += ' ' * lb
2025-07-01 03:05:16.510 else:
2025-07-01 03:05:16.522 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:16.527 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:16.532 else:
2025-07-01 03:05:16.537 # the synch pair is identical
2025-07-01 03:05:16.541 yield '  ' + aelt
2025-07-01 03:05:16.546
2025-07-01 03:05:16.551 # pump out diffs from after the synch point
2025-07-01 03:05:16.560 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:16.565
2025-07-01 03:05:16.570 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:16.575 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:16.580
2025-07-01 03:05:16.586 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:16.593 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:16.599 alo = 431, ahi = 1101
2025-07-01 03:05:16.606 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:16.611 blo = 431, bhi = 1101
2025-07-01 03:05:16.616
2025-07-01 03:05:16.620 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:16.625 g = []
2025-07-01 03:05:16.630 if alo < ahi:
2025-07-01 03:05:16.634 if blo < bhi:
2025-07-01 03:05:16.640 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:16.645 else:
2025-07-01 03:05:16.651 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:16.656 elif blo < bhi:
2025-07-01 03:05:16.661 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:16.666
2025-07-01 03:05:16.671 >       yield from g
2025-07-01 03:05:16.676
2025-07-01 03:05:16.681 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:16.686 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:16.691
2025-07-01 03:05:16.696 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:16.702 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:16.706 alo = 431, ahi = 1101
2025-07-01 03:05:16.711 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:16.716 blo = 431, bhi = 1101
2025-07-01 03:05:16.720
2025-07-01 03:05:16.725 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:16.729 r"""
2025-07-01 03:05:16.734 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:16.739 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:16.743 synch point, and intraline difference marking is done on the
2025-07-01 03:05:16.748 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:16.753
2025-07-01 03:05:16.758 Example:
2025-07-01 03:05:16.763
2025-07-01 03:05:16.767 >>> d = Differ()
2025-07-01 03:05:16.772 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:16.777 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:16.781 >>> print(''.join(results), end="")
2025-07-01 03:05:16.785 - abcDefghiJkl
2025-07-01 03:05:16.794 + abcdefGhijkl
2025-07-01 03:05:16.805 """
2025-07-01 03:05:16.811
2025-07-01 03:05:16.816 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:16.822 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:16.830 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:16.835 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:16.840 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:16.845
2025-07-01 03:05:16.849 # search for the pair that matches best without being identical
2025-07-01 03:05:16.854 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:16.859 # on junk -- unless we have to)
2025-07-01 03:05:16.863 for j in range(blo, bhi):
2025-07-01 03:05:16.868 bj = b[j]
2025-07-01 03:05:16.873 cruncher.set_seq2(bj)
2025-07-01 03:05:16.877 for i in range(alo, ahi):
2025-07-01 03:05:16.882 ai = a[i]
2025-07-01 03:05:16.886 if ai == bj:
2025-07-01 03:05:16.891 if eqi is None:
2025-07-01 03:05:16.895 eqi, eqj = i, j
2025-07-01 03:05:16.900 continue
2025-07-01 03:05:16.905 cruncher.set_seq1(ai)
2025-07-01 03:05:16.911 # computing similarity is expensive, so use the quick
2025-07-01 03:05:16.916 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:16.920 # compares by a factor of 3.
2025-07-01 03:05:16.926 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:16.932 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:16.937 # of the computation is cached by cruncher
2025-07-01 03:05:16.941 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:16.946 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:16.950 cruncher.ratio() > best_ratio:
2025-07-01 03:05:16.955 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:16.960 if best_ratio < cutoff:
2025-07-01 03:05:16.965 # no non-identical "pretty close" pair
2025-07-01 03:05:16.970 if eqi is None:
2025-07-01 03:05:16.975 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:16.980 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:16.984 return
2025-07-01 03:05:16.989 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:16.994 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:16.998 else:
2025-07-01 03:05:17.003 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:17.008 eqi = None
2025-07-01 03:05:17.013
2025-07-01 03:05:17.017 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:17.022 # identical
2025-07-01 03:05:17.026
2025-07-01 03:05:17.031 # pump out diffs from before the synch point
2025-07-01 03:05:17.035 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:17.039
2025-07-01 03:05:17.044 # do intraline marking on the synch pair
2025-07-01 03:05:17.049 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:17.054 if eqi is None:
2025-07-01 03:05:17.058 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:17.063 atags = btags = ""
2025-07-01 03:05:17.069 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:17.082 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:17.094 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:17.106 if tag == 'replace':
2025-07-01 03:05:17.114 atags += '^' * la
2025-07-01 03:05:17.126 btags += '^' * lb
2025-07-01 03:05:17.138 elif tag == 'delete':
2025-07-01 03:05:17.154 atags += '-' * la
2025-07-01 03:05:17.162 elif tag == 'insert':
2025-07-01 03:05:17.172 btags += '+' * lb
2025-07-01 03:05:17.182 elif tag == 'equal':
2025-07-01 03:05:17.198 atags += ' ' * la
2025-07-01 03:05:17.206 btags += ' ' * lb
2025-07-01 03:05:17.216 else:
2025-07-01 03:05:17.230 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:17.243 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:17.258 else:
2025-07-01 03:05:17.274 # the synch pair is identical
2025-07-01 03:05:17.287 yield '  ' + aelt
2025-07-01 03:05:17.303
2025-07-01 03:05:17.314 # pump out diffs from after the synch point
2025-07-01 03:05:17.322 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:17.334
2025-07-01 03:05:17.340 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:17.345 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:17.349
2025-07-01 03:05:17.354 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:17.359 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:17.364 alo = 432, ahi = 1101
2025-07-01 03:05:17.369 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:17.375 blo = 432, bhi = 1101
2025-07-01 03:05:17.380
2025-07-01 03:05:17.384 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:17.389 g = []
2025-07-01 03:05:17.394 if alo < ahi:
2025-07-01 03:05:17.399 if blo < bhi:
2025-07-01 03:05:17.405 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:17.411 else:
2025-07-01 03:05:17.416 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:17.421 elif blo < bhi:
2025-07-01 03:05:17.426 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:17.431
2025-07-01 03:05:17.436 >       yield from g
2025-07-01 03:05:17.440
2025-07-01 03:05:17.445 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:17.450 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:17.455
2025-07-01 03:05:17.461 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:17.466 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:17.471 alo = 432, ahi = 1101
2025-07-01 03:05:17.477 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:17.482 blo = 432, bhi = 1101
2025-07-01 03:05:17.487
2025-07-01 03:05:17.492 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:17.496 r"""
2025-07-01 03:05:17.501 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:17.507 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:17.512 synch point, and intraline difference marking is done on the
2025-07-01 03:05:17.517 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:17.521
2025-07-01 03:05:17.526 Example:
2025-07-01 03:05:17.531
2025-07-01 03:05:17.536 >>> d = Differ()
2025-07-01 03:05:17.540 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:17.545 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:17.550 >>> print(''.join(results), end="")
2025-07-01 03:05:17.555 - abcDefghiJkl
2025-07-01 03:05:17.564 + abcdefGhijkl
2025-07-01 03:05:17.573 """
2025-07-01 03:05:17.578
2025-07-01 03:05:17.583 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:17.588 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:17.593 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:17.598 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:17.602 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:17.607
2025-07-01 03:05:17.612 # search for the pair that matches best without being identical
2025-07-01 03:05:17.616 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:17.621 # on junk -- unless we have to)
2025-07-01 03:05:17.625 for j in range(blo, bhi):
2025-07-01 03:05:17.630 bj = b[j]
2025-07-01 03:05:17.635 cruncher.set_seq2(bj)
2025-07-01 03:05:17.641 for i in range(alo, ahi):
2025-07-01 03:05:17.646 ai = a[i]
2025-07-01 03:05:17.653 if ai == bj:
2025-07-01 03:05:17.662 if eqi is None:
2025-07-01 03:05:17.674 eqi, eqj = i, j
2025-07-01 03:05:17.686 continue
2025-07-01 03:05:17.698 cruncher.set_seq1(ai)
2025-07-01 03:05:17.706 # computing similarity is expensive, so use the quick
2025-07-01 03:05:17.718 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:17.734 # compares by a factor of 3.
2025-07-01 03:05:17.742 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:17.754 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:17.766 # of the computation is cached by cruncher
2025-07-01 03:05:17.774 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:17.790 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:17.798 cruncher.ratio() > best_ratio:
2025-07-01 03:05:17.814 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:17.822 if best_ratio < cutoff:
2025-07-01 03:05:17.831 # no non-identical "pretty close" pair
2025-07-01 03:05:17.842 if eqi is None:
2025-07-01 03:05:17.854 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:17.865 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:17.882 return
2025-07-01 03:05:17.893 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:17.902 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:17.910 else:
2025-07-01 03:05:17.918 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:17.927 eqi = None
2025-07-01 03:05:17.947
2025-07-01 03:05:17.958 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:17.972 # identical
2025-07-01 03:05:17.982
2025-07-01 03:05:17.994 # pump out diffs from before the synch point
2025-07-01 03:05:18.005 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:18.018
2025-07-01 03:05:18.026 # do intraline marking on the synch pair
2025-07-01 03:05:18.042 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:18.050 if eqi is None:
2025-07-01 03:05:18.062 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:18.074 atags = btags = ""
2025-07-01 03:05:18.083 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:18.098 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:18.110 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:18.118 if tag == 'replace':
2025-07-01 03:05:18.134 atags += '^' * la
2025-07-01 03:05:18.142 btags += '^' * lb
2025-07-01 03:05:18.150 elif tag == 'delete':
2025-07-01 03:05:18.162 atags += '-' * la
2025-07-01 03:05:18.172 elif tag == 'insert':
2025-07-01 03:05:18.186 btags += '+' * lb
2025-07-01 03:05:18.198 elif tag == 'equal':
2025-07-01 03:05:18.206 atags += ' ' * la
2025-07-01 03:05:18.216 btags += ' ' * lb
2025-07-01 03:05:18.237 else:
2025-07-01 03:05:18.250 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:18.258 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:18.274 else:
2025-07-01 03:05:18.282 # the synch pair is identical
2025-07-01 03:05:18.287 yield '  ' + aelt
2025-07-01 03:05:18.309
2025-07-01 03:05:18.322 # pump out diffs from after the synch point
2025-07-01 03:05:18.330 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:18.338
2025-07-01 03:05:18.346 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:18.358 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:18.370
2025-07-01 03:05:18.378 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:18.394 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:18.402 alo = 433, ahi = 1101
2025-07-01 03:05:18.416 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:18.422 blo = 433, bhi = 1101
2025-07-01 03:05:18.434
2025-07-01 03:05:18.449 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:18.462 g = []
2025-07-01 03:05:18.472 if alo < ahi:
2025-07-01 03:05:18.482 if blo < bhi:
2025-07-01 03:05:18.494 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:18.502 else:
2025-07-01 03:05:18.514 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:18.526 elif blo < bhi:
2025-07-01 03:05:18.534 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:18.546
2025-07-01 03:05:18.558 >       yield from g
2025-07-01 03:05:18.566
2025-07-01 03:05:18.578 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:18.586 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:18.596
2025-07-01 03:05:18.610 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:18.622 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:18.634 alo = 433, ahi = 1101
2025-07-01 03:05:18.646 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:18.654 blo = 433, bhi = 1101
2025-07-01 03:05:18.666
2025-07-01 03:05:18.682 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:18.690 r"""
2025-07-01 03:05:18.702 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:18.714 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:18.723 synch point, and intraline difference marking is done on the
2025-07-01 03:05:18.742 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:18.750
2025-07-01 03:05:18.766 Example:
2025-07-01 03:05:18.773
2025-07-01 03:05:18.788 >>> d = Differ()
2025-07-01 03:05:18.793 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:18.804 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:18.819 >>> print(''.join(results), end="")
2025-07-01 03:05:18.834 - abcDefghiJkl
2025-07-01 03:05:18.858 + abcdefGhijkl
2025-07-01 03:05:18.882 """
2025-07-01 03:05:18.890
2025-07-01 03:05:18.906 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:18.913 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:18.917 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:18.926 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:18.938 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:18.946
2025-07-01 03:05:18.958 # search for the pair that matches best without being identical
2025-07-01 03:05:18.974 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:18.982 # on junk -- unless we have to)
2025-07-01 03:05:18.994 for j in range(blo, bhi):
2025-07-01 03:05:19.003 bj = b[j]
2025-07-01 03:05:19.017 cruncher.set_seq2(bj)
2025-07-01 03:05:19.030 for i in range(alo, ahi):
2025-07-01 03:05:19.038 ai = a[i]
2025-07-01 03:05:19.047 if ai == bj:
2025-07-01 03:05:19.062 if eqi is None:
2025-07-01 03:05:19.070 eqi, eqj = i, j
2025-07-01 03:05:19.078 continue
2025-07-01 03:05:19.090 cruncher.set_seq1(ai)
2025-07-01 03:05:19.098 # computing similarity is expensive, so use the quick
2025-07-01 03:05:19.109 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:19.117 # compares by a factor of 3.
2025-07-01 03:05:19.131 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:19.145 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:19.166 # of the computation is cached by cruncher
2025-07-01 03:05:19.174 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:19.190 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:19.202 cruncher.ratio() > best_ratio:
2025-07-01 03:05:19.210 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:19.224 if best_ratio < cutoff:
2025-07-01 03:05:19.238 # no non-identical "pretty close" pair
2025-07-01 03:05:19.252 if eqi is None:
2025-07-01 03:05:19.269 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:19.280 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:19.294 return
2025-07-01 03:05:19.302 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:19.314 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:19.330 else:
2025-07-01 03:05:19.339 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:19.351 eqi = None
2025-07-01 03:05:19.366
2025-07-01 03:05:19.374 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:19.382 # identical
2025-07-01 03:05:19.394
2025-07-01 03:05:19.400 # pump out diffs from before the synch point
2025-07-01 03:05:19.414 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:19.422
2025-07-01 03:05:19.434 # do intraline marking on the synch pair
2025-07-01 03:05:19.441 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:19.458 if eqi is None:
2025-07-01 03:05:19.466 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:19.473 atags = btags = ""
2025-07-01 03:05:19.490 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:19.498 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:19.510 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:19.519 if tag == 'replace':
2025-07-01 03:05:19.534 atags += '^' * la
2025-07-01 03:05:19.546 btags += '^' * lb
2025-07-01 03:05:19.554 elif tag == 'delete':
2025-07-01 03:05:19.565 atags += '-' * la
2025-07-01 03:05:19.584 elif tag == 'insert':
2025-07-01 03:05:19.594 btags += '+' * lb
2025-07-01 03:05:19.606 elif tag == 'equal':
2025-07-01 03:05:19.618 atags += ' ' * la
2025-07-01 03:05:19.630 btags += ' ' * lb
2025-07-01 03:05:19.642 else:
2025-07-01 03:05:19.648 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:19.666 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:19.674 else:
2025-07-01 03:05:19.686 # the synch pair is identical
2025-07-01 03:05:19.696 yield '  ' + aelt
2025-07-01 03:05:19.712
2025-07-01 03:05:19.721 # pump out diffs from after the synch point
2025-07-01 03:05:19.733 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:19.739
2025-07-01 03:05:19.748 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:19.766 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:19.773
2025-07-01 03:05:19.782 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:19.798 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:19.805 alo = 434, ahi = 1101
2025-07-01 03:05:19.818 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:19.826 blo = 434, bhi = 1101
2025-07-01 03:05:19.834
2025-07-01 03:05:19.846 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:19.862 g = []
2025-07-01 03:05:19.878 if alo < ahi:
2025-07-01 03:05:19.891 if blo < bhi:
2025-07-01 03:05:19.906 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:19.918 else:
2025-07-01 03:05:19.932 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:19.944 elif blo < bhi:
2025-07-01 03:05:19.952 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:19.966
2025-07-01 03:05:19.978 >       yield from g
2025-07-01 03:05:19.986
2025-07-01 03:05:19.997 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:20.004 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:20.009
2025-07-01 03:05:20.025 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:20.044 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:20.056 alo = 434, ahi = 1101
2025-07-01 03:05:20.071 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:20.080 blo = 434, bhi = 1101
2025-07-01 03:05:20.094
2025-07-01 03:05:20.106 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:20.114 r"""
2025-07-01 03:05:20.129 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:20.142 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:20.150 synch point, and intraline difference marking is done on the
2025-07-01 03:05:20.158 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:20.174
2025-07-01 03:05:20.182 Example:
2025-07-01 03:05:20.194
2025-07-01 03:05:20.202 >>> d = Differ()
2025-07-01 03:05:20.214 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:20.221 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:20.230 >>> print(''.join(results), end="")
2025-07-01 03:05:20.246 - abcDefghiJkl
2025-07-01 03:05:20.262 + abcdefGhijkl
2025-07-01 03:05:20.286 """
2025-07-01 03:05:20.298
2025-07-01 03:05:20.310 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:20.322 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:20.334 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:20.341 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:20.358 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:20.380
2025-07-01 03:05:20.394 # search for the pair that matches best without being identical
2025-07-01 03:05:20.406 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:20.420 # on junk -- unless we have to)
2025-07-01 03:05:20.431 for j in range(blo, bhi):
2025-07-01 03:05:20.442 bj = b[j]
2025-07-01 03:05:20.447 cruncher.set_seq2(bj)
2025-07-01 03:05:20.461 for i in range(alo, ahi):
2025-07-01 03:05:20.470 ai = a[i]
2025-07-01 03:05:20.477 if ai == bj:
2025-07-01 03:05:20.485 if eqi is None:
2025-07-01 03:05:20.500 eqi, eqj = i, j
2025-07-01 03:05:20.521 continue
2025-07-01 03:05:20.534 cruncher.set_seq1(ai)
2025-07-01 03:05:20.546 # computing similarity is expensive, so use the quick
2025-07-01 03:05:20.554 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:20.566 # compares by a factor of 3.
2025-07-01 03:05:20.574 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:20.586 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:20.602 # of the computation is cached by cruncher
2025-07-01 03:05:20.614 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:20.622 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:20.634 cruncher.ratio() > best_ratio:
2025-07-01 03:05:20.646 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:20.651 if best_ratio < cutoff:
2025-07-01 03:05:20.656 # no non-identical "pretty close" pair
2025-07-01 03:05:20.660 if eqi is None:
2025-07-01 03:05:20.665 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:20.670 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:20.674 return
2025-07-01 03:05:20.679 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:20.683 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:20.687 else:
2025-07-01 03:05:20.692 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:20.696 eqi = None
2025-07-01 03:05:20.701
2025-07-01 03:05:20.706 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:20.710 # identical
2025-07-01 03:05:20.714
2025-07-01 03:05:20.719 # pump out diffs from before the synch point
2025-07-01 03:05:20.723 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:20.728
2025-07-01 03:05:20.732 # do intraline marking on the synch pair
2025-07-01 03:05:20.737 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:20.741 if eqi is None:
2025-07-01 03:05:20.745 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:20.750 atags = btags = ""
2025-07-01 03:05:20.754 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:20.759 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:20.763 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:20.768 if tag == 'replace':
2025-07-01 03:05:20.772 atags += '^' * la
2025-07-01 03:05:20.776 btags += '^' * lb
2025-07-01 03:05:20.781 elif tag == 'delete':
2025-07-01 03:05:20.785 atags += '-' * la
2025-07-01 03:05:20.789 elif tag == 'insert':
2025-07-01 03:05:20.794 btags += '+' * lb
2025-07-01 03:05:20.798 elif tag == 'equal':
2025-07-01 03:05:20.802 atags += ' ' * la
2025-07-01 03:05:20.807 btags += ' ' * lb
2025-07-01 03:05:20.811 else:
2025-07-01 03:05:20.815 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:20.820 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:20.824 else:
2025-07-01 03:05:20.828 # the synch pair is identical
2025-07-01 03:05:20.833 yield '  ' + aelt
2025-07-01 03:05:20.837
2025-07-01 03:05:20.842 # pump out diffs from after the synch point
2025-07-01 03:05:20.847 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:20.851
2025-07-01 03:05:20.855 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:20.860 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:20.864
2025-07-01 03:05:20.869 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:20.873 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:20.878 alo = 435, ahi = 1101
2025-07-01 03:05:20.882 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:20.887 blo = 435, bhi = 1101
2025-07-01 03:05:20.891
2025-07-01 03:05:20.895 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:20.899 g = []
2025-07-01 03:05:20.904 if alo < ahi:
2025-07-01 03:05:20.908 if blo < bhi:
2025-07-01 03:05:20.912 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:20.917 else:
2025-07-01 03:05:20.921 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:20.926 elif blo < bhi:
2025-07-01 03:05:20.930 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:20.934
2025-07-01 03:05:20.939 >       yield from g
2025-07-01 03:05:20.943
2025-07-01 03:05:20.947 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:20.953 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:20.957
2025-07-01 03:05:20.962 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:20.966 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:20.971 alo = 435, ahi = 1101
2025-07-01 03:05:20.975 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:20.980 blo = 435, bhi = 1101
2025-07-01 03:05:20.984
2025-07-01 03:05:20.989 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:20.993 r"""
2025-07-01 03:05:20.997 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:21.002 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:21.007 synch point, and intraline difference marking is done on the
2025-07-01 03:05:21.011 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:21.015
2025-07-01 03:05:21.020 Example:
2025-07-01 03:05:21.024
2025-07-01 03:05:21.029 >>> d = Differ()
2025-07-01 03:05:21.033 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:21.038 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:21.042 >>> print(''.join(results), end="")
2025-07-01 03:05:21.047 - abcDefghiJkl
2025-07-01 03:05:21.056 + abcdefGhijkl
2025-07-01 03:05:21.065 """
2025-07-01 03:05:21.069
2025-07-01 03:05:21.073 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:21.078 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:21.082 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:21.087 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:21.092 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:21.096
2025-07-01 03:05:21.100 # search for the pair that matches best without being identical
2025-07-01 03:05:21.105 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:21.110 # on junk -- unless we have to)
2025-07-01 03:05:21.114 for j in range(blo, bhi):
2025-07-01 03:05:21.118 bj = b[j]
2025-07-01 03:05:21.123 cruncher.set_seq2(bj)
2025-07-01 03:05:21.128 for i in range(alo, ahi):
2025-07-01 03:05:21.132 ai = a[i]
2025-07-01 03:05:21.137 if ai == bj:
2025-07-01 03:05:21.141 if eqi is None:
2025-07-01 03:05:21.146 eqi, eqj = i, j
2025-07-01 03:05:21.150 continue
2025-07-01 03:05:21.154 cruncher.set_seq1(ai)
2025-07-01 03:05:21.159 # computing similarity is expensive, so use the quick
2025-07-01 03:05:21.164 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:21.169 # compares by a factor of 3.
2025-07-01 03:05:21.173 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:21.178 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:21.183 # of the computation is cached by cruncher
2025-07-01 03:05:21.187 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:21.191 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:21.196 cruncher.ratio() > best_ratio:
2025-07-01 03:05:21.200 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:21.204 if best_ratio < cutoff:
2025-07-01 03:05:21.209 # no non-identical "pretty close" pair
2025-07-01 03:05:21.213 if eqi is None:
2025-07-01 03:05:21.218 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:21.222 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:21.226 return
2025-07-01 03:05:21.231 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:21.235 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:21.239 else:
2025-07-01 03:05:21.243 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:21.248 eqi = None
2025-07-01 03:05:21.252
2025-07-01 03:05:21.256 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:21.260 # identical
2025-07-01 03:05:21.265
2025-07-01 03:05:21.269 # pump out diffs from before the synch point
2025-07-01 03:05:21.273 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:21.278
2025-07-01 03:05:21.282 # do intraline marking on the synch pair
2025-07-01 03:05:21.286 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:21.290 if eqi is None:
2025-07-01 03:05:21.295 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:21.299 atags = btags = ""
2025-07-01 03:05:21.303 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:21.307 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:21.312 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:21.316 if tag == 'replace':
2025-07-01 03:05:21.321 atags += '^' * la
2025-07-01 03:05:21.325 btags += '^' * lb
2025-07-01 03:05:21.329 elif tag == 'delete':
2025-07-01 03:05:21.334 atags += '-' * la
2025-07-01 03:05:21.338 elif tag == 'insert':
2025-07-01 03:05:21.342 btags += '+' * lb
2025-07-01 03:05:21.346 elif tag == 'equal':
2025-07-01 03:05:21.351 atags += ' ' * la
2025-07-01 03:05:21.355 btags += ' ' * lb
2025-07-01 03:05:21.359 else:
2025-07-01 03:05:21.364 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:21.368 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:21.373 else:
2025-07-01 03:05:21.377 # the synch pair is identical
2025-07-01 03:05:21.382 yield '  ' + aelt
2025-07-01 03:05:21.386
2025-07-01 03:05:21.390 # pump out diffs from after the synch point
2025-07-01 03:05:21.394 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:21.398
2025-07-01 03:05:21.403 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:21.407 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:21.412
2025-07-01 03:05:21.417 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:21.422 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:21.426 alo = 436, ahi = 1101
2025-07-01 03:05:21.431 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:21.435 blo = 436, bhi = 1101
2025-07-01 03:05:21.440
2025-07-01 03:05:21.444 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:21.448 g = []
2025-07-01 03:05:21.452 if alo < ahi:
2025-07-01 03:05:21.457 if blo < bhi:
2025-07-01 03:05:21.461 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:21.466 else:
2025-07-01 03:05:21.471 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:21.475 elif blo < bhi:
2025-07-01 03:05:21.479 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:21.483
2025-07-01 03:05:21.488 >       yield from g
2025-07-01 03:05:21.492
2025-07-01 03:05:21.496 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:21.501 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:21.505
2025-07-01 03:05:21.509 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:21.514 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:21.519 alo = 436, ahi = 1101
2025-07-01 03:05:21.523 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:21.527 blo = 436, bhi = 1101
2025-07-01 03:05:21.532
2025-07-01 03:05:21.536 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:21.541 r"""
2025-07-01 03:05:21.545 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:21.550 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:21.554 synch point, and intraline difference marking is done on the
2025-07-01 03:05:21.558 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:21.563
2025-07-01 03:05:21.567 Example:
2025-07-01 03:05:21.571
2025-07-01 03:05:21.575 >>> d = Differ()
2025-07-01 03:05:21.580 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:21.584 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:21.589 >>> print(''.join(results), end="")
2025-07-01 03:05:21.593 - abcDefghiJkl
2025-07-01 03:05:21.602 + abcdefGhijkl
2025-07-01 03:05:21.612 """
2025-07-01 03:05:21.616
2025-07-01 03:05:21.621 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:21.625 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:21.629 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:21.634 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:21.638 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:21.642
2025-07-01 03:05:21.646 # search for the pair that matches best without being identical
2025-07-01 03:05:21.651 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:21.655 # on junk -- unless we have to)
2025-07-01 03:05:21.659 for j in range(blo, bhi):
2025-07-01 03:05:21.663 bj = b[j]
2025-07-01 03:05:21.668 cruncher.set_seq2(bj)
2025-07-01 03:05:21.672 for i in range(alo, ahi):
2025-07-01 03:05:21.676 ai = a[i]
2025-07-01 03:05:21.680 if ai == bj:
2025-07-01 03:05:21.685 if eqi is None:
2025-07-01 03:05:21.689 eqi, eqj = i, j
2025-07-01 03:05:21.693 continue
2025-07-01 03:05:21.697 cruncher.set_seq1(ai)
2025-07-01 03:05:21.702 # computing similarity is expensive, so use the quick
2025-07-01 03:05:21.706 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:21.711 # compares by a factor of 3.
2025-07-01 03:05:21.715 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:21.719 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:21.724 # of the computation is cached by cruncher
2025-07-01 03:05:21.728 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:21.732 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:21.737 cruncher.ratio() > best_ratio:
2025-07-01 03:05:21.741 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:21.745 if best_ratio < cutoff:
2025-07-01 03:05:21.749 # no non-identical "pretty close" pair
2025-07-01 03:05:21.754 if eqi is None:
2025-07-01 03:05:21.758 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:21.763 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:21.767 return
2025-07-01 03:05:21.772 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:21.776 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:21.780 else:
2025-07-01 03:05:21.784 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:21.789 eqi = None
2025-07-01 03:05:21.793
2025-07-01 03:05:21.797 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:21.801 # identical
2025-07-01 03:05:21.806
2025-07-01 03:05:21.810 # pump out diffs from before the synch point
2025-07-01 03:05:21.814 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:21.819
2025-07-01 03:05:21.823 # do intraline marking on the synch pair
2025-07-01 03:05:21.827 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:21.831 if eqi is None:
2025-07-01 03:05:21.835 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:21.840 atags = btags = ""
2025-07-01 03:05:21.844 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:21.849 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:21.853 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:21.857 if tag == 'replace':
2025-07-01 03:05:21.861 atags += '^' * la
2025-07-01 03:05:21.866 btags += '^' * lb
2025-07-01 03:05:21.870 elif tag == 'delete':
2025-07-01 03:05:21.874 atags += '-' * la
2025-07-01 03:05:21.878 elif tag == 'insert':
2025-07-01 03:05:21.883 btags += '+' * lb
2025-07-01 03:05:21.887 elif tag == 'equal':
2025-07-01 03:05:21.891 atags += ' ' * la
2025-07-01 03:05:21.895 btags += ' ' * lb
2025-07-01 03:05:21.900 else:
2025-07-01 03:05:21.905 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:21.909 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:21.913 else:
2025-07-01 03:05:21.918 # the synch pair is identical
2025-07-01 03:05:21.922 yield '  ' + aelt
2025-07-01 03:05:21.926
2025-07-01 03:05:21.930 # pump out diffs from after the synch point
2025-07-01 03:05:21.934 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:21.939
2025-07-01 03:05:21.943 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:21.947 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:21.951
2025-07-01 03:05:21.955 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:21.960 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:21.964 alo = 437, ahi = 1101
2025-07-01 03:05:21.969 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:21.973 blo = 437, bhi = 1101
2025-07-01 03:05:21.977
2025-07-01 03:05:21.982 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:21.986 g = []
2025-07-01 03:05:21.990 if alo < ahi:
2025-07-01 03:05:21.994 if blo < bhi:
2025-07-01 03:05:21.998 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:22.002 else:
2025-07-01 03:05:22.007 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:22.011 elif blo < bhi:
2025-07-01 03:05:22.015 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:22.019
2025-07-01 03:05:22.024 >       yield from g
2025-07-01 03:05:22.028
2025-07-01 03:05:22.033 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:22.037 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:22.041
2025-07-01 03:05:22.046 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:22.050 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:22.054 alo = 437, ahi = 1101
2025-07-01 03:05:22.059 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:22.063 blo = 437, bhi = 1101
2025-07-01 03:05:22.067
2025-07-01 03:05:22.072 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:22.076 r"""
2025-07-01 03:05:22.080 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:22.085 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:22.089 synch point, and intraline difference marking is done on the
2025-07-01 03:05:22.093 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:22.097
2025-07-01 03:05:22.102 Example:
2025-07-01 03:05:22.106
2025-07-01 03:05:22.110 >>> d = Differ()
2025-07-01 03:05:22.114 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:22.119 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:22.123 >>> print(''.join(results), end="")
2025-07-01 03:05:22.127 - abcDefghiJkl
2025-07-01 03:05:22.136 + abcdefGhijkl
2025-07-01 03:05:22.144 """
2025-07-01 03:05:22.148
2025-07-01 03:05:22.152 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:22.158 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:22.162 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:22.166 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:22.171 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:22.175
2025-07-01 03:05:22.180 # search for the pair that matches best without being identical
2025-07-01 03:05:22.184 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:22.188 # on junk -- unless we have to)
2025-07-01 03:05:22.193 for j in range(blo, bhi):
2025-07-01 03:05:22.197 bj = b[j]
2025-07-01 03:05:22.201 cruncher.set_seq2(bj)
2025-07-01 03:05:22.206 for i in range(alo, ahi):
2025-07-01 03:05:22.211 ai = a[i]
2025-07-01 03:05:22.215 if ai == bj:
2025-07-01 03:05:22.219 if eqi is None:
2025-07-01 03:05:22.224 eqi, eqj = i, j
2025-07-01 03:05:22.228 continue
2025-07-01 03:05:22.233 cruncher.set_seq1(ai)
2025-07-01 03:05:22.237 # computing similarity is expensive, so use the quick
2025-07-01 03:05:22.242 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:22.246 # compares by a factor of 3.
2025-07-01 03:05:22.251 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:22.255 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:22.259 # of the computation is cached by cruncher
2025-07-01 03:05:22.264 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:22.268 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:22.272 cruncher.ratio() > best_ratio:
2025-07-01 03:05:22.277 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:22.281 if best_ratio < cutoff:
2025-07-01 03:05:22.286 # no non-identical "pretty close" pair
2025-07-01 03:05:22.292 if eqi is None:
2025-07-01 03:05:22.297 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:22.304 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:22.310 return
2025-07-01 03:05:22.317 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:22.323 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:22.329 else:
2025-07-01 03:05:22.336 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:22.340 eqi = None
2025-07-01 03:05:22.345
2025-07-01 03:05:22.349 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:22.354 # identical
2025-07-01 03:05:22.358
2025-07-01 03:05:22.363 # pump out diffs from before the synch point
2025-07-01 03:05:22.368 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:22.372
2025-07-01 03:05:22.377 # do intraline marking on the synch pair
2025-07-01 03:05:22.381 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:22.386 if eqi is None:
2025-07-01 03:05:22.390 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:22.395 atags = btags = ""
2025-07-01 03:05:22.400 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:22.404 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:22.409 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:22.413 if tag == 'replace':
2025-07-01 03:05:22.418 atags += '^' * la
2025-07-01 03:05:22.423 btags += '^' * lb
2025-07-01 03:05:22.428 elif tag == 'delete':
2025-07-01 03:05:22.432 atags += '-' * la
2025-07-01 03:05:22.437 elif tag == 'insert':
2025-07-01 03:05:22.442 btags += '+' * lb
2025-07-01 03:05:22.447 elif tag == 'equal':
2025-07-01 03:05:22.451 atags += ' ' * la
2025-07-01 03:05:22.456 btags += ' ' * lb
2025-07-01 03:05:22.460 else:
2025-07-01 03:05:22.465 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:22.470 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:22.475 else:
2025-07-01 03:05:22.480 # the synch pair is identical
2025-07-01 03:05:22.485 yield '  ' + aelt
2025-07-01 03:05:22.489
2025-07-01 03:05:22.494 # pump out diffs from after the synch point
2025-07-01 03:05:22.499 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:22.503
2025-07-01 03:05:22.508 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:22.512 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:22.517
2025-07-01 03:05:22.522 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:22.527 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:22.531 alo = 438, ahi = 1101
2025-07-01 03:05:22.536 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:22.541 blo = 438, bhi = 1101
2025-07-01 03:05:22.545
2025-07-01 03:05:22.550 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:22.555 g = []
2025-07-01 03:05:22.559 if alo < ahi:
2025-07-01 03:05:22.564 if blo < bhi:
2025-07-01 03:05:22.568 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:22.573 else:
2025-07-01 03:05:22.577 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:22.582 elif blo < bhi:
2025-07-01 03:05:22.587 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:22.591
2025-07-01 03:05:22.596 >       yield from g
2025-07-01 03:05:22.601
2025-07-01 03:05:22.605 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:22.610 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:22.615
2025-07-01 03:05:22.620 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:22.625 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:22.629 alo = 438, ahi = 1101
2025-07-01 03:05:22.634 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:22.639 blo = 438, bhi = 1101
2025-07-01 03:05:22.644
2025-07-01 03:05:22.650 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:22.656 r"""
2025-07-01 03:05:22.662 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:22.667 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:22.672 synch point, and intraline difference marking is done on the
2025-07-01 03:05:22.676 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:22.681
2025-07-01 03:05:22.685 Example:
2025-07-01 03:05:22.689
2025-07-01 03:05:22.694 >>> d = Differ()
2025-07-01 03:05:22.698 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:22.703 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:22.707 >>> print(''.join(results), end="")
2025-07-01 03:05:22.711 - abcDefghiJkl
2025-07-01 03:05:22.720 + abcdefGhijkl
2025-07-01 03:05:22.728 """
2025-07-01 03:05:22.733
2025-07-01 03:05:22.737 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:22.741 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:22.746 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:22.750 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:22.755 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:22.759
2025-07-01 03:05:22.763 # search for the pair that matches best without being identical
2025-07-01 03:05:22.768 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:22.772 # on junk -- unless we have to)
2025-07-01 03:05:22.776 for j in range(blo, bhi):
2025-07-01 03:05:22.781 bj = b[j]
2025-07-01 03:05:22.785 cruncher.set_seq2(bj)
2025-07-01 03:05:22.790 for i in range(alo, ahi):
2025-07-01 03:05:22.794 ai = a[i]
2025-07-01 03:05:22.798 if ai == bj:
2025-07-01 03:05:22.803 if eqi is None:
2025-07-01 03:05:22.807 eqi, eqj = i, j
2025-07-01 03:05:22.811 continue
2025-07-01 03:05:22.816 cruncher.set_seq1(ai)
2025-07-01 03:05:22.820 # computing similarity is expensive, so use the quick
2025-07-01 03:05:22.824 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:22.829 # compares by a factor of 3.
2025-07-01 03:05:22.833 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:22.838 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:22.843 # of the computation is cached by cruncher
2025-07-01 03:05:22.847 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:22.852 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:22.856 cruncher.ratio() > best_ratio:
2025-07-01 03:05:22.860 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:22.865 if best_ratio < cutoff:
2025-07-01 03:05:22.869 # no non-identical "pretty close" pair
2025-07-01 03:05:22.873 if eqi is None:
2025-07-01 03:05:22.878 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:22.882 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:22.886 return
2025-07-01 03:05:22.891 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:22.896 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:22.900 else:
2025-07-01 03:05:22.905 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:22.909 eqi = None
2025-07-01 03:05:22.913
2025-07-01 03:05:22.917 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:22.922 # identical
2025-07-01 03:05:22.926
2025-07-01 03:05:22.930 # pump out diffs from before the synch point
2025-07-01 03:05:22.935 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:22.939
2025-07-01 03:05:22.943 # do intraline marking on the synch pair
2025-07-01 03:05:22.948 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:22.953 if eqi is None:
2025-07-01 03:05:22.957 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:22.962 atags = btags = ""
2025-07-01 03:05:22.966 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:22.972 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:22.976 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:22.981 if tag == 'replace':
2025-07-01 03:05:22.985 atags += '^' * la
2025-07-01 03:05:22.989 btags += '^' * lb
2025-07-01 03:05:22.994 elif tag == 'delete':
2025-07-01 03:05:22.998 atags += '-' * la
2025-07-01 03:05:23.002 elif tag == 'insert':
2025-07-01 03:05:23.007 btags += '+' * lb
2025-07-01 03:05:23.012 elif tag == 'equal':
2025-07-01 03:05:23.016 atags += ' ' * la
2025-07-01 03:05:23.021 btags += ' ' * lb
2025-07-01 03:05:23.025 else:
2025-07-01 03:05:23.030 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:23.034 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:23.038 else:
2025-07-01 03:05:23.043 # the synch pair is identical
2025-07-01 03:05:23.047 yield '  ' + aelt
2025-07-01 03:05:23.051
2025-07-01 03:05:23.056 # pump out diffs from after the synch point
2025-07-01 03:05:23.060 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:23.065
2025-07-01 03:05:23.069 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:23.073 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:23.078
2025-07-01 03:05:23.082 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:23.087 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:23.091 alo = 439, ahi = 1101
2025-07-01 03:05:23.096 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:23.101 blo = 439, bhi = 1101
2025-07-01 03:05:23.105
2025-07-01 03:05:23.109 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:23.113 g = []
2025-07-01 03:05:23.118 if alo < ahi:
2025-07-01 03:05:23.122 if blo < bhi:
2025-07-01 03:05:23.126 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:23.130 else:
2025-07-01 03:05:23.135 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:23.139 elif blo < bhi:
2025-07-01 03:05:23.144 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:23.148
2025-07-01 03:05:23.153 >       yield from g
2025-07-01 03:05:23.158
2025-07-01 03:05:23.163 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:23.167 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:23.173
2025-07-01 03:05:23.181 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:23.187 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:23.193 alo = 439, ahi = 1101
2025-07-01 03:05:23.198 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:23.202 blo = 439, bhi = 1101
2025-07-01 03:05:23.206
2025-07-01 03:05:23.210 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:23.215 r"""
2025-07-01 03:05:23.219 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:23.223 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:23.228 synch point, and intraline difference marking is done on the
2025-07-01 03:05:23.232 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:23.236
2025-07-01 03:05:23.240 Example:
2025-07-01 03:05:23.244
2025-07-01 03:05:23.249 >>> d = Differ()
2025-07-01 03:05:23.253 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:23.257 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:23.261 >>> print(''.join(results), end="")
2025-07-01 03:05:23.266 - abcDefghiJkl
2025-07-01 03:05:23.274 + abcdefGhijkl
2025-07-01 03:05:23.283 """
2025-07-01 03:05:23.287
2025-07-01 03:05:23.291 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:23.295 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:23.300 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:23.304 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:23.308 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:23.312
2025-07-01 03:05:23.317 # search for the pair that matches best without being identical
2025-07-01 03:05:23.321 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:23.325 # on junk -- unless we have to)
2025-07-01 03:05:23.330 for j in range(blo, bhi):
2025-07-01 03:05:23.334 bj = b[j]
2025-07-01 03:05:23.338 cruncher.set_seq2(bj)
2025-07-01 03:05:23.343 for i in range(alo, ahi):
2025-07-01 03:05:23.347 ai = a[i]
2025-07-01 03:05:23.351 if ai == bj:
2025-07-01 03:05:23.355 if eqi is None:
2025-07-01 03:05:23.360 eqi, eqj = i, j
2025-07-01 03:05:23.364 continue
2025-07-01 03:05:23.368 cruncher.set_seq1(ai)
2025-07-01 03:05:23.373 # computing similarity is expensive, so use the quick
2025-07-01 03:05:23.377 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:23.382 # compares by a factor of 3.
2025-07-01 03:05:23.386 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:23.391 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:23.396 # of the computation is cached by cruncher
2025-07-01 03:05:23.400 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:23.405 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:23.409 cruncher.ratio() > best_ratio:
2025-07-01 03:05:23.414 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:23.418 if best_ratio < cutoff:
2025-07-01 03:05:23.423 # no non-identical "pretty close" pair
2025-07-01 03:05:23.428 if eqi is None:
2025-07-01 03:05:23.433 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:23.438 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:23.442 return
2025-07-01 03:05:23.446 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:23.451 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:23.455 else:
2025-07-01 03:05:23.460 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:23.464 eqi = None
2025-07-01 03:05:23.468
2025-07-01 03:05:23.473 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:23.477 # identical
2025-07-01 03:05:23.481
2025-07-01 03:05:23.486 # pump out diffs from before the synch point
2025-07-01 03:05:23.491 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:23.495
2025-07-01 03:05:23.499 # do intraline marking on the synch pair
2025-07-01 03:05:23.504 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:23.508 if eqi is None:
2025-07-01 03:05:23.512 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:23.517 atags = btags = ""
2025-07-01 03:05:23.521 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:23.525 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:23.530 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:23.534 if tag == 'replace':
2025-07-01 03:05:23.538 atags += '^' * la
2025-07-01 03:05:23.543 btags += '^' * lb
2025-07-01 03:05:23.548 elif tag == 'delete':
2025-07-01 03:05:23.554 atags += '-' * la
2025-07-01 03:05:23.561 elif tag == 'insert':
2025-07-01 03:05:23.567 btags += '+' * lb
2025-07-01 03:05:23.572 elif tag == 'equal':
2025-07-01 03:05:23.577 atags += ' ' * la
2025-07-01 03:05:23.582 btags += ' ' * lb
2025-07-01 03:05:23.587 else:
2025-07-01 03:05:23.592 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:23.597 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:23.601 else:
2025-07-01 03:05:23.605 # the synch pair is identical
2025-07-01 03:05:23.610 yield '  ' + aelt
2025-07-01 03:05:23.614
2025-07-01 03:05:23.618 # pump out diffs from after the synch point
2025-07-01 03:05:23.623 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:23.627
2025-07-01 03:05:23.631 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:23.636 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:23.640
2025-07-01 03:05:23.645 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:23.649 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:23.654 alo = 440, ahi = 1101
2025-07-01 03:05:23.659 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:23.665 blo = 440, bhi = 1101
2025-07-01 03:05:23.669
2025-07-01 03:05:23.674 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:23.678 g = []
2025-07-01 03:05:23.683 if alo < ahi:
2025-07-01 03:05:23.688 if blo < bhi:
2025-07-01 03:05:23.692 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:23.697 else:
2025-07-01 03:05:23.701 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:23.705 elif blo < bhi:
2025-07-01 03:05:23.710 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:23.715
2025-07-01 03:05:23.719 >       yield from g
2025-07-01 03:05:23.724
2025-07-01 03:05:23.728 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:23.733 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:23.737
2025-07-01 03:05:23.742 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:23.747 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:23.752 alo = 440, ahi = 1101
2025-07-01 03:05:23.758 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:23.763 blo = 440, bhi = 1101
2025-07-01 03:05:23.767
2025-07-01 03:05:23.772 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:23.776 r"""
2025-07-01 03:05:23.780 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:23.786 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:23.791 synch point, and intraline difference marking is done on the
2025-07-01 03:05:23.795 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:23.800
2025-07-01 03:05:23.804 Example:
2025-07-01 03:05:23.809
2025-07-01 03:05:23.813 >>> d = Differ()
2025-07-01 03:05:23.818 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:23.823 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:23.827 >>> print(''.join(results), end="")
2025-07-01 03:05:23.831 - abcDefghiJkl
2025-07-01 03:05:23.840 + abcdefGhijkl
2025-07-01 03:05:23.849 """
2025-07-01 03:05:23.853
2025-07-01 03:05:23.858 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:23.862 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:23.866 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:23.871 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:23.875 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:23.880
2025-07-01 03:05:23.885 # search for the pair that matches best without being identical
2025-07-01 03:05:23.889 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:23.894 # on junk -- unless we have to)
2025-07-01 03:05:23.898 for j in range(blo, bhi):
2025-07-01 03:05:23.903 bj = b[j]
2025-07-01 03:05:23.907 cruncher.set_seq2(bj)
2025-07-01 03:05:23.912 for i in range(alo, ahi):
2025-07-01 03:05:23.916 ai = a[i]
2025-07-01 03:05:23.921 if ai == bj:
2025-07-01 03:05:23.925 if eqi is None:
2025-07-01 03:05:23.930 eqi, eqj = i, j
2025-07-01 03:05:23.934 continue
2025-07-01 03:05:23.938 cruncher.set_seq1(ai)
2025-07-01 03:05:23.943 # computing similarity is expensive, so use the quick
2025-07-01 03:05:23.947 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:23.951 # compares by a factor of 3.
2025-07-01 03:05:23.956 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:23.960 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:23.965 # of the computation is cached by cruncher
2025-07-01 03:05:23.969 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:23.974 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:23.978 cruncher.ratio() > best_ratio:
2025-07-01 03:05:23.983 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:23.987 if best_ratio < cutoff:
2025-07-01 03:05:23.991 # no non-identical "pretty close" pair
2025-07-01 03:05:23.996 if eqi is None:
2025-07-01 03:05:24.000 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:24.005 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:24.009 return
2025-07-01 03:05:24.014 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:24.019 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:24.023 else:
2025-07-01 03:05:24.027 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:24.032 eqi = None
2025-07-01 03:05:24.036
2025-07-01 03:05:24.041 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:24.045 # identical
2025-07-01 03:05:24.049
2025-07-01 03:05:24.054 # pump out diffs from before the synch point
2025-07-01 03:05:24.058 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:24.063
2025-07-01 03:05:24.067 # do intraline marking on the synch pair
2025-07-01 03:05:24.072 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:24.076 if eqi is None:
2025-07-01 03:05:24.081 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:24.086 atags = btags = ""
2025-07-01 03:05:24.090 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:24.095 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:24.099 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:24.103 if tag == 'replace':
2025-07-01 03:05:24.108 atags += '^' * la
2025-07-01 03:05:24.112 btags += '^' * lb
2025-07-01 03:05:24.117 elif tag == 'delete':
2025-07-01 03:05:24.121 atags += '-' * la
2025-07-01 03:05:24.126 elif tag == 'insert':
2025-07-01 03:05:24.131 btags += '+' * lb
2025-07-01 03:05:24.135 elif tag == 'equal':
2025-07-01 03:05:24.139 atags += ' ' * la
2025-07-01 03:05:24.144 btags += ' ' * lb
2025-07-01 03:05:24.148 else:
2025-07-01 03:05:24.152 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:24.157 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:24.161 else:
2025-07-01 03:05:24.165 # the synch pair is identical
2025-07-01 03:05:24.170 yield '  ' + aelt
2025-07-01 03:05:24.174
2025-07-01 03:05:24.179 # pump out diffs from after the synch point
2025-07-01 03:05:24.183 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:24.188
2025-07-01 03:05:24.192 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:24.196 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:24.200
2025-07-01 03:05:24.205 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:24.210 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:24.214 alo = 441, ahi = 1101
2025-07-01 03:05:24.219 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:24.223 blo = 441, bhi = 1101
2025-07-01 03:05:24.227
2025-07-01 03:05:24.232 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:24.236 g = []
2025-07-01 03:05:24.240 if alo < ahi:
2025-07-01 03:05:24.244 if blo < bhi:
2025-07-01 03:05:24.249 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:24.253 else:
2025-07-01 03:05:24.257 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:24.261 elif blo < bhi:
2025-07-01 03:05:24.266 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:24.270
2025-07-01 03:05:24.274 >       yield from g
2025-07-01 03:05:24.278
2025-07-01 03:05:24.282 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:24.287 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:24.291
2025-07-01 03:05:24.295 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:24.304 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:24.310 alo = 441, ahi = 1101
2025-07-01 03:05:24.318 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:24.323 blo = 441, bhi = 1101
2025-07-01 03:05:24.327
2025-07-01 03:05:24.332 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:24.336 r"""
2025-07-01 03:05:24.340 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:24.345 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:24.349 synch point, and intraline difference marking is done on the
2025-07-01 03:05:24.353 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:24.358
2025-07-01 03:05:24.362 Example:
2025-07-01 03:05:24.366
2025-07-01 03:05:24.370 >>> d = Differ()
2025-07-01 03:05:24.374 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:24.379 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:24.384 >>> print(''.join(results), end="")
2025-07-01 03:05:24.388 - abcDefghiJkl
2025-07-01 03:05:24.397 + abcdefGhijkl
2025-07-01 03:05:24.405 """
2025-07-01 03:05:24.410
2025-07-01 03:05:24.414 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:24.419 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:24.423 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:24.427 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:24.432 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:24.436
2025-07-01 03:05:24.440 # search for the pair that matches best without being identical
2025-07-01 03:05:24.445 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:24.449 # on junk -- unless we have to)
2025-07-01 03:05:24.453 for j in range(blo, bhi):
2025-07-01 03:05:24.458 bj = b[j]
2025-07-01 03:05:24.462 cruncher.set_seq2(bj)
2025-07-01 03:05:24.467 for i in range(alo, ahi):
2025-07-01 03:05:24.471 ai = a[i]
2025-07-01 03:05:24.475 if ai == bj:
2025-07-01 03:05:24.480 if eqi is None:
2025-07-01 03:05:24.484 eqi, eqj = i, j
2025-07-01 03:05:24.489 continue
2025-07-01 03:05:24.493 cruncher.set_seq1(ai)
2025-07-01 03:05:24.498 # computing similarity is expensive, so use the quick
2025-07-01 03:05:24.502 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:24.507 # compares by a factor of 3.
2025-07-01 03:05:24.512 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:24.517 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:24.521 # of the computation is cached by cruncher
2025-07-01 03:05:24.526 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:24.530 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:24.535 cruncher.ratio() > best_ratio:
2025-07-01 03:05:24.540 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:24.544 if best_ratio < cutoff:
2025-07-01 03:05:24.549 # no non-identical "pretty close" pair
2025-07-01 03:05:24.553 if eqi is None:
2025-07-01 03:05:24.558 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:24.562 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:24.567 return
2025-07-01 03:05:24.571 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:24.576 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:24.580 else:
2025-07-01 03:05:24.584 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:24.589 eqi = None
2025-07-01 03:05:24.593
2025-07-01 03:05:24.597 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:24.602 # identical
2025-07-01 03:05:24.606
2025-07-01 03:05:24.610 # pump out diffs from before the synch point
2025-07-01 03:05:24.615 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:24.620
2025-07-01 03:05:24.624 # do intraline marking on the synch pair
2025-07-01 03:05:24.629 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:24.633 if eqi is None:
2025-07-01 03:05:24.638 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:24.642 atags = btags = ""
2025-07-01 03:05:24.647 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:24.651 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:24.656 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:24.661 if tag == 'replace':
2025-07-01 03:05:24.665 atags += '^' * la
2025-07-01 03:05:24.670 btags += '^' * lb
2025-07-01 03:05:24.674 elif tag == 'delete':
2025-07-01 03:05:24.679 atags += '-' * la
2025-07-01 03:05:24.683 elif tag == 'insert':
2025-07-01 03:05:24.688 btags += '+' * lb
2025-07-01 03:05:24.693 elif tag == 'equal':
2025-07-01 03:05:24.698 atags += ' ' * la
2025-07-01 03:05:24.703 btags += ' ' * lb
2025-07-01 03:05:24.708 else:
2025-07-01 03:05:24.713 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:24.717 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:24.722 else:
2025-07-01 03:05:24.727 # the synch pair is identical
2025-07-01 03:05:24.732 yield '  ' + aelt
2025-07-01 03:05:24.737
2025-07-01 03:05:24.742 # pump out diffs from after the synch point
2025-07-01 03:05:24.747 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:24.752
2025-07-01 03:05:24.756 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:24.761 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:24.766
2025-07-01 03:05:24.771 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:24.776 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:24.781 alo = 444, ahi = 1101
2025-07-01 03:05:24.786 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:24.791 blo = 444, bhi = 1101
2025-07-01 03:05:24.796
2025-07-01 03:05:24.801 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:24.805 g = []
2025-07-01 03:05:24.810 if alo < ahi:
2025-07-01 03:05:24.815 if blo < bhi:
2025-07-01 03:05:24.820 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:24.824 else:
2025-07-01 03:05:24.829 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:24.833 elif blo < bhi:
2025-07-01 03:05:24.838 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:24.843
2025-07-01 03:05:24.847 >       yield from g
2025-07-01 03:05:24.852
2025-07-01 03:05:24.857 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:24.861 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:24.866
2025-07-01 03:05:24.870 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:24.875 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:24.880 alo = 444, ahi = 1101
2025-07-01 03:05:24.885 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:24.890 blo = 444, bhi = 1101
2025-07-01 03:05:24.894
2025-07-01 03:05:24.899 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:24.903 r"""
2025-07-01 03:05:24.908 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:24.913 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:24.917 synch point, and intraline difference marking is done on the
2025-07-01 03:05:24.922 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:24.926
2025-07-01 03:05:24.930 Example:
2025-07-01 03:05:24.934
2025-07-01 03:05:24.939 >>> d = Differ()
2025-07-01 03:05:24.943 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:24.947 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:24.952 >>> print(''.join(results), end="")
2025-07-01 03:05:24.956 - abcDefghiJkl
2025-07-01 03:05:24.964 + abcdefGhijkl
2025-07-01 03:05:24.973 """
2025-07-01 03:05:24.977
2025-07-01 03:05:24.982 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:24.986 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:24.991 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:24.995 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:25.000 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:25.004
2025-07-01 03:05:25.009 # search for the pair that matches best without being identical
2025-07-01 03:05:25.013 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:25.018 # on junk -- unless we have to)
2025-07-01 03:05:25.022 for j in range(blo, bhi):
2025-07-01 03:05:25.027 bj = b[j]
2025-07-01 03:05:25.032 cruncher.set_seq2(bj)
2025-07-01 03:05:25.036 for i in range(alo, ahi):
2025-07-01 03:05:25.042 ai = a[i]
2025-07-01 03:05:25.046 if ai == bj:
2025-07-01 03:05:25.050 if eqi is None:
2025-07-01 03:05:25.055 eqi, eqj = i, j
2025-07-01 03:05:25.059 continue
2025-07-01 03:05:25.064 cruncher.set_seq1(ai)
2025-07-01 03:05:25.068 # computing similarity is expensive, so use the quick
2025-07-01 03:05:25.073 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:25.077 # compares by a factor of 3.
2025-07-01 03:05:25.082 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:25.086 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:25.091 # of the computation is cached by cruncher
2025-07-01 03:05:25.095 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:25.099 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:25.104 cruncher.ratio() > best_ratio:
2025-07-01 03:05:25.108 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:25.113 if best_ratio < cutoff:
2025-07-01 03:05:25.117 # no non-identical "pretty close" pair
2025-07-01 03:05:25.122 if eqi is None:
2025-07-01 03:05:25.126 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:25.130 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:25.135 return
2025-07-01 03:05:25.139 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:25.145 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:25.149 else:
2025-07-01 03:05:25.154 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:25.158 eqi = None
2025-07-01 03:05:25.163
2025-07-01 03:05:25.167 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:25.171 # identical
2025-07-01 03:05:25.176
2025-07-01 03:05:25.180 # pump out diffs from before the synch point
2025-07-01 03:05:25.185 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:25.189
2025-07-01 03:05:25.194 # do intraline marking on the synch pair
2025-07-01 03:05:25.198 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:25.203 if eqi is None:
2025-07-01 03:05:25.207 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:25.212 atags = btags = ""
2025-07-01 03:05:25.216 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:25.221 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:25.225 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:25.230 if tag == 'replace':
2025-07-01 03:05:25.234 atags += '^' * la
2025-07-01 03:05:25.238 btags += '^' * lb
2025-07-01 03:05:25.243 elif tag == 'delete':
2025-07-01 03:05:25.247 atags += '-' * la
2025-07-01 03:05:25.251 elif tag == 'insert':
2025-07-01 03:05:25.256 btags += '+' * lb
2025-07-01 03:05:25.263 elif tag == 'equal':
2025-07-01 03:05:25.269 atags += ' ' * la
2025-07-01 03:05:25.276 btags += ' ' * lb
2025-07-01 03:05:25.282 else:
2025-07-01 03:05:25.288 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:25.295 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:25.301 else:
2025-07-01 03:05:25.306 # the synch pair is identical
2025-07-01 03:05:25.310 yield '  ' + aelt
2025-07-01 03:05:25.315
2025-07-01 03:05:25.319 # pump out diffs from after the synch point
2025-07-01 03:05:25.324 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:25.329
2025-07-01 03:05:25.333 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:25.337 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:25.342
2025-07-01 03:05:25.346 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:25.351 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:25.356 alo = 445, ahi = 1101
2025-07-01 03:05:25.361 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:25.366 blo = 445, bhi = 1101
2025-07-01 03:05:25.370
2025-07-01 03:05:25.374 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:25.378 g = []
2025-07-01 03:05:25.383 if alo < ahi:
2025-07-01 03:05:25.387 if blo < bhi:
2025-07-01 03:05:25.392 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:25.396 else:
2025-07-01 03:05:25.400 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:25.405 elif blo < bhi:
2025-07-01 03:05:25.409 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:25.413
2025-07-01 03:05:25.417 >       yield from g
2025-07-01 03:05:25.425
2025-07-01 03:05:25.429 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:25.434 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:25.438
2025-07-01 03:05:25.442 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:25.447 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:25.451 alo = 445, ahi = 1101
2025-07-01 03:05:25.456 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:25.461 blo = 445, bhi = 1101
2025-07-01 03:05:25.465
2025-07-01 03:05:25.469 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:25.474 r"""
2025-07-01 03:05:25.478 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:25.483 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:25.487 synch point, and intraline difference marking is done on the
2025-07-01 03:05:25.492 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:25.496
2025-07-01 03:05:25.500 Example:
2025-07-01 03:05:25.505
2025-07-01 03:05:25.509 >>> d = Differ()
2025-07-01 03:05:25.513 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:25.518 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:25.522 >>> print(''.join(results), end="")
2025-07-01 03:05:25.527 - abcDefghiJkl
2025-07-01 03:05:25.536 + abcdefGhijkl
2025-07-01 03:05:25.544 """
2025-07-01 03:05:25.548
2025-07-01 03:05:25.553 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:25.558 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:25.563 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:25.567 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:25.572 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:25.576
2025-07-01 03:05:25.581 # search for the pair that matches best without being identical
2025-07-01 03:05:25.585 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:25.590 # on junk -- unless we have to)
2025-07-01 03:05:25.594 for j in range(blo, bhi):
2025-07-01 03:05:25.598 bj = b[j]
2025-07-01 03:05:25.603 cruncher.set_seq2(bj)
2025-07-01 03:05:25.607 for i in range(alo, ahi):
2025-07-01 03:05:25.611 ai = a[i]
2025-07-01 03:05:25.616 if ai == bj:
2025-07-01 03:05:25.620 if eqi is None:
2025-07-01 03:05:25.625 eqi, eqj = i, j
2025-07-01 03:05:25.630 continue
2025-07-01 03:05:25.634 cruncher.set_seq1(ai)
2025-07-01 03:05:25.639 # computing similarity is expensive, so use the quick
2025-07-01 03:05:25.643 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:25.648 # compares by a factor of 3.
2025-07-01 03:05:25.652 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:25.657 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:25.661 # of the computation is cached by cruncher
2025-07-01 03:05:25.666 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:25.671 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:25.675 cruncher.ratio() > best_ratio:
2025-07-01 03:05:25.680 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:25.685 if best_ratio < cutoff:
2025-07-01 03:05:25.689 # no non-identical "pretty close" pair
2025-07-01 03:05:25.694 if eqi is None:
2025-07-01 03:05:25.699 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:25.704 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:25.709 return
2025-07-01 03:05:25.714 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:25.719 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:25.724 else:
2025-07-01 03:05:25.730 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:25.734 eqi = None
2025-07-01 03:05:25.738
2025-07-01 03:05:25.743 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:25.747 # identical
2025-07-01 03:05:25.752
2025-07-01 03:05:25.756 # pump out diffs from before the synch point
2025-07-01 03:05:25.761 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:25.765
2025-07-01 03:05:25.769 # do intraline marking on the synch pair
2025-07-01 03:05:25.774 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:25.778 if eqi is None:
2025-07-01 03:05:25.783 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:25.787 atags = btags = ""
2025-07-01 03:05:25.791 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:25.796 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:25.800 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:25.805 if tag == 'replace':
2025-07-01 03:05:25.809 atags += '^' * la
2025-07-01 03:05:25.813 btags += '^' * lb
2025-07-01 03:05:25.818 elif tag == 'delete':
2025-07-01 03:05:25.822 atags += '-' * la
2025-07-01 03:05:25.826 elif tag == 'insert':
2025-07-01 03:05:25.831 btags += '+' * lb
2025-07-01 03:05:25.836 elif tag == 'equal':
2025-07-01 03:05:25.840 atags += ' ' * la
2025-07-01 03:05:25.844 btags += ' ' * lb
2025-07-01 03:05:25.849 else:
2025-07-01 03:05:25.854 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:25.858 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:25.863 else:
2025-07-01 03:05:25.867 # the synch pair is identical
2025-07-01 03:05:25.871 yield '  ' + aelt
2025-07-01 03:05:25.876
2025-07-01 03:05:25.880 # pump out diffs from after the synch point
2025-07-01 03:05:25.885 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:25.890
2025-07-01 03:05:25.894 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:25.899 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:25.903
2025-07-01 03:05:25.908 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:25.912 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:25.917 alo = 446, ahi = 1101
2025-07-01 03:05:25.922 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:25.927 blo = 446, bhi = 1101
2025-07-01 03:05:25.933
2025-07-01 03:05:25.938 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:25.942 g = []
2025-07-01 03:05:25.946 if alo < ahi:
2025-07-01 03:05:25.951 if blo < bhi:
2025-07-01 03:05:25.955 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:25.960 else:
2025-07-01 03:05:25.964 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:25.968 elif blo < bhi:
2025-07-01 03:05:25.973 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:25.977
2025-07-01 03:05:25.982 >       yield from g
2025-07-01 03:05:25.986
2025-07-01 03:05:25.990 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:25.995 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:25.999
2025-07-01 03:05:26.004 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:26.009 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:26.013 alo = 446, ahi = 1101
2025-07-01 03:05:26.018 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:26.023 blo = 446, bhi = 1101
2025-07-01 03:05:26.028
2025-07-01 03:05:26.033 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:26.037 r"""
2025-07-01 03:05:26.041 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:26.046 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:26.050 synch point, and intraline difference marking is done on the
2025-07-01 03:05:26.055 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:26.060
2025-07-01 03:05:26.064 Example:
2025-07-01 03:05:26.069
2025-07-01 03:05:26.073 >>> d = Differ()
2025-07-01 03:05:26.077 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:26.082 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:26.086 >>> print(''.join(results), end="")
2025-07-01 03:05:26.091 - abcDefghiJkl
2025-07-01 03:05:26.099 + abcdefGhijkl
2025-07-01 03:05:26.108 """
2025-07-01 03:05:26.113
2025-07-01 03:05:26.117 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:26.122 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:26.126 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:26.130 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:26.135 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:26.140
2025-07-01 03:05:26.145 # search for the pair that matches best without being identical
2025-07-01 03:05:26.150 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:26.154 # on junk -- unless we have to)
2025-07-01 03:05:26.159 for j in range(blo, bhi):
2025-07-01 03:05:26.163 bj = b[j]
2025-07-01 03:05:26.167 cruncher.set_seq2(bj)
2025-07-01 03:05:26.172 for i in range(alo, ahi):
2025-07-01 03:05:26.176 ai = a[i]
2025-07-01 03:05:26.181 if ai == bj:
2025-07-01 03:05:26.185 if eqi is None:
2025-07-01 03:05:26.190 eqi, eqj = i, j
2025-07-01 03:05:26.195 continue
2025-07-01 03:05:26.199 cruncher.set_seq1(ai)
2025-07-01 03:05:26.204 # computing similarity is expensive, so use the quick
2025-07-01 03:05:26.209 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:26.213 # compares by a factor of 3.
2025-07-01 03:05:26.218 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:26.223 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:26.228 # of the computation is cached by cruncher
2025-07-01 03:05:26.232 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:26.237 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:26.241 cruncher.ratio() > best_ratio:
2025-07-01 03:05:26.246 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:26.250 if best_ratio < cutoff:
2025-07-01 03:05:26.254 # no non-identical "pretty close" pair
2025-07-01 03:05:26.259 if eqi is None:
2025-07-01 03:05:26.263 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:26.268 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:26.273 return
2025-07-01 03:05:26.277 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:26.282 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:26.286 else:
2025-07-01 03:05:26.291 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:26.295 eqi = None
2025-07-01 03:05:26.300
2025-07-01 03:05:26.304 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:26.309 # identical
2025-07-01 03:05:26.313
2025-07-01 03:05:26.318 # pump out diffs from before the synch point
2025-07-01 03:05:26.322 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:26.327
2025-07-01 03:05:26.331 # do intraline marking on the synch pair
2025-07-01 03:05:26.337 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:26.344 if eqi is None:
2025-07-01 03:05:26.348 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:26.353 atags = btags = ""
2025-07-01 03:05:26.357 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:26.362 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:26.366 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:26.371 if tag == 'replace':
2025-07-01 03:05:26.375 atags += '^' * la
2025-07-01 03:05:26.379 btags += '^' * lb
2025-07-01 03:05:26.384 elif tag == 'delete':
2025-07-01 03:05:26.388 atags += '-' * la
2025-07-01 03:05:26.393 elif tag == 'insert':
2025-07-01 03:05:26.397 btags += '+' * lb
2025-07-01 03:05:26.402 elif tag == 'equal':
2025-07-01 03:05:26.406 atags += ' ' * la
2025-07-01 03:05:26.410 btags += ' ' * lb
2025-07-01 03:05:26.415 else:
2025-07-01 03:05:26.419 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:26.423 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:26.428 else:
2025-07-01 03:05:26.432 # the synch pair is identical
2025-07-01 03:05:26.437 yield '  ' + aelt
2025-07-01 03:05:26.442
2025-07-01 03:05:26.446 # pump out diffs from after the synch point
2025-07-01 03:05:26.451 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:26.455
2025-07-01 03:05:26.460 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:26.464 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:26.469
2025-07-01 03:05:26.473 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:26.478 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:26.482 alo = 447, ahi = 1101
2025-07-01 03:05:26.487 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:26.492 blo = 447, bhi = 1101
2025-07-01 03:05:26.496
2025-07-01 03:05:26.501 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:26.505 g = []
2025-07-01 03:05:26.510 if alo < ahi:
2025-07-01 03:05:26.514 if blo < bhi:
2025-07-01 03:05:26.519 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:26.523 else:
2025-07-01 03:05:26.528 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:26.533 elif blo < bhi:
2025-07-01 03:05:26.538 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:26.544
2025-07-01 03:05:26.549 >       yield from g
2025-07-01 03:05:26.554
2025-07-01 03:05:26.559 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:26.563 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:26.568
2025-07-01 03:05:26.573 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:26.577 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:26.582 alo = 447, ahi = 1101
2025-07-01 03:05:26.588 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:26.593 blo = 447, bhi = 1101
2025-07-01 03:05:26.597
2025-07-01 03:05:26.602 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:26.608 r"""
2025-07-01 03:05:26.614 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:26.620 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:26.625 synch point, and intraline difference marking is done on the
2025-07-01 03:05:26.631 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:26.637
2025-07-01 03:05:26.643 Example:
2025-07-01 03:05:26.648
2025-07-01 03:05:26.652 >>> d = Differ()
2025-07-01 03:05:26.657 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:26.662 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:26.666 >>> print(''.join(results), end="")
2025-07-01 03:05:26.671 - abcDefghiJkl
2025-07-01 03:05:26.680 + abcdefGhijkl
2025-07-01 03:05:26.689 """
2025-07-01 03:05:26.693
2025-07-01 03:05:26.698 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:26.703 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:26.707 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:26.711 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:26.716 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:26.720
2025-07-01 03:05:26.725 # search for the pair that matches best without being identical
2025-07-01 03:05:26.729 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:26.734 # on junk -- unless we have to)
2025-07-01 03:05:26.738 for j in range(blo, bhi):
2025-07-01 03:05:26.742 bj = b[j]
2025-07-01 03:05:26.748 cruncher.set_seq2(bj)
2025-07-01 03:05:26.753 for i in range(alo, ahi):
2025-07-01 03:05:26.758 ai = a[i]
2025-07-01 03:05:26.762 if ai == bj:
2025-07-01 03:05:26.767 if eqi is None:
2025-07-01 03:05:26.772 eqi, eqj = i, j
2025-07-01 03:05:26.776 continue
2025-07-01 03:05:26.780 cruncher.set_seq1(ai)
2025-07-01 03:05:26.785 # computing similarity is expensive, so use the quick
2025-07-01 03:05:26.789 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:26.794 # compares by a factor of 3.
2025-07-01 03:05:26.798 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:26.803 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:26.807 # of the computation is cached by cruncher
2025-07-01 03:05:26.811 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:26.816 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:26.820 cruncher.ratio() > best_ratio:
2025-07-01 03:05:26.824 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:26.829 if best_ratio < cutoff:
2025-07-01 03:05:26.833 # no non-identical "pretty close" pair
2025-07-01 03:05:26.837 if eqi is None:
2025-07-01 03:05:26.842 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:26.847 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:26.851 return
2025-07-01 03:05:26.855 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:26.860 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:26.864 else:
2025-07-01 03:05:26.868 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:26.873 eqi = None
2025-07-01 03:05:26.877
2025-07-01 03:05:26.881 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:26.886 # identical
2025-07-01 03:05:26.890
2025-07-01 03:05:26.894 # pump out diffs from before the synch point
2025-07-01 03:05:26.899 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:26.903
2025-07-01 03:05:26.908 # do intraline marking on the synch pair
2025-07-01 03:05:26.912 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:26.917 if eqi is None:
2025-07-01 03:05:26.921 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:26.926 atags = btags = ""
2025-07-01 03:05:26.930 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:26.935 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:26.939 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:26.943 if tag == 'replace':
2025-07-01 03:05:26.948 atags += '^' * la
2025-07-01 03:05:26.952 btags += '^' * lb
2025-07-01 03:05:26.957 elif tag == 'delete':
2025-07-01 03:05:26.962 atags += '-' * la
2025-07-01 03:05:26.967 elif tag == 'insert':
2025-07-01 03:05:26.971 btags += '+' * lb
2025-07-01 03:05:26.975 elif tag == 'equal':
2025-07-01 03:05:26.980 atags += ' ' * la
2025-07-01 03:05:26.984 btags += ' ' * lb
2025-07-01 03:05:26.989 else:
2025-07-01 03:05:26.993 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:26.998 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:27.002 else:
2025-07-01 03:05:27.007 # the synch pair is identical
2025-07-01 03:05:27.011 yield '  ' + aelt
2025-07-01 03:05:27.015
2025-07-01 03:05:27.020 # pump out diffs from after the synch point
2025-07-01 03:05:27.024 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:27.028
2025-07-01 03:05:27.033 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:27.037 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:27.041
2025-07-01 03:05:27.046 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:27.051 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:27.056 alo = 448, ahi = 1101
2025-07-01 03:05:27.060 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:27.065 blo = 448, bhi = 1101
2025-07-01 03:05:27.070
2025-07-01 03:05:27.074 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:27.079 g = []
2025-07-01 03:05:27.083 if alo < ahi:
2025-07-01 03:05:27.087 if blo < bhi:
2025-07-01 03:05:27.092 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:27.096 else:
2025-07-01 03:05:27.101 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:27.105 elif blo < bhi:
2025-07-01 03:05:27.109 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:27.113
2025-07-01 03:05:27.118 >       yield from g
2025-07-01 03:05:27.122
2025-07-01 03:05:27.126 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:27.131 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:27.135
2025-07-01 03:05:27.140 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:27.145 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:27.149 alo = 448, ahi = 1101
2025-07-01 03:05:27.154 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:27.158 blo = 448, bhi = 1101
2025-07-01 03:05:27.163
2025-07-01 03:05:27.168 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:27.172 r"""
2025-07-01 03:05:27.177 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:27.181 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:27.185 synch point, and intraline difference marking is done on the
2025-07-01 03:05:27.190 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:27.194
2025-07-01 03:05:27.198 Example:
2025-07-01 03:05:27.203
2025-07-01 03:05:27.207 >>> d = Differ()
2025-07-01 03:05:27.212 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:27.216 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:27.221 >>> print(''.join(results), end="")
2025-07-01 03:05:27.225 - abcDefghiJkl
2025-07-01 03:05:27.234 + abcdefGhijkl
2025-07-01 03:05:27.242 """
2025-07-01 03:05:27.247
2025-07-01 03:05:27.251 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:27.256 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:27.260 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:27.265 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:27.269 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:27.273
2025-07-01 03:05:27.278 # search for the pair that matches best without being identical
2025-07-01 03:05:27.282 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:27.287 # on junk -- unless we have to)
2025-07-01 03:05:27.291 for j in range(blo, bhi):
2025-07-01 03:05:27.296 bj = b[j]
2025-07-01 03:05:27.300 cruncher.set_seq2(bj)
2025-07-01 03:05:27.305 for i in range(alo, ahi):
2025-07-01 03:05:27.309 ai = a[i]
2025-07-01 03:05:27.314 if ai == bj:
2025-07-01 03:05:27.318 if eqi is None:
2025-07-01 03:05:27.323 eqi, eqj = i, j
2025-07-01 03:05:27.327 continue
2025-07-01 03:05:27.332 cruncher.set_seq1(ai)
2025-07-01 03:05:27.336 # computing similarity is expensive, so use the quick
2025-07-01 03:05:27.341 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:27.345 # compares by a factor of 3.
2025-07-01 03:05:27.349 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:27.354 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:27.358 # of the computation is cached by cruncher
2025-07-01 03:05:27.363 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:27.367 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:27.371 cruncher.ratio() > best_ratio:
2025-07-01 03:05:27.376 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:27.380 if best_ratio < cutoff:
2025-07-01 03:05:27.384 # no non-identical "pretty close" pair
2025-07-01 03:05:27.389 if eqi is None:
2025-07-01 03:05:27.394 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:27.398 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:27.403 return
2025-07-01 03:05:27.407 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:27.412 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:27.416 else:
2025-07-01 03:05:27.420 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:27.425 eqi = None
2025-07-01 03:05:27.429
2025-07-01 03:05:27.434 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:27.438 # identical
2025-07-01 03:05:27.442
2025-07-01 03:05:27.447 # pump out diffs from before the synch point
2025-07-01 03:05:27.451 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:27.456
2025-07-01 03:05:27.460 # do intraline marking on the synch pair
2025-07-01 03:05:27.465 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:27.469 if eqi is None:
2025-07-01 03:05:27.473 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:27.478 atags = btags = ""
2025-07-01 03:05:27.482 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:27.486 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:27.491 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:27.496 if tag == 'replace':
2025-07-01 03:05:27.501 atags += '^' * la
2025-07-01 03:05:27.506 btags += '^' * lb
2025-07-01 03:05:27.511 elif tag == 'delete':
2025-07-01 03:05:27.516 atags += '-' * la
2025-07-01 03:05:27.520 elif tag == 'insert':
2025-07-01 03:05:27.525 btags += '+' * lb
2025-07-01 03:05:27.529 elif tag == 'equal':
2025-07-01 03:05:27.534 atags += ' ' * la
2025-07-01 03:05:27.538 btags += ' ' * lb
2025-07-01 03:05:27.543 else:
2025-07-01 03:05:27.547 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:27.552 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:27.556 else:
2025-07-01 03:05:27.561 # the synch pair is identical
2025-07-01 03:05:27.565 yield '  ' + aelt
2025-07-01 03:05:27.569
2025-07-01 03:05:27.574 # pump out diffs from after the synch point
2025-07-01 03:05:27.578 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:27.583
2025-07-01 03:05:27.587 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:27.592 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:27.596
2025-07-01 03:05:27.600 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:27.605 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:27.609 alo = 449, ahi = 1101
2025-07-01 03:05:27.614 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:27.618 blo = 449, bhi = 1101
2025-07-01 03:05:27.623
2025-07-01 03:05:27.627 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:27.631 g = []
2025-07-01 03:05:27.635 if alo < ahi:
2025-07-01 03:05:27.640 if blo < bhi:
2025-07-01 03:05:27.644 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:27.649 else:
2025-07-01 03:05:27.653 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:27.658 elif blo < bhi:
2025-07-01 03:05:27.663 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:27.667
2025-07-01 03:05:27.671 >       yield from g
2025-07-01 03:05:27.675
2025-07-01 03:05:27.680 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:27.685 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:27.689
2025-07-01 03:05:27.693 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:27.698 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:27.702 alo = 449, ahi = 1101
2025-07-01 03:05:27.707 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:27.711 blo = 449, bhi = 1101
2025-07-01 03:05:27.715
2025-07-01 03:05:27.720 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:27.725 r"""
2025-07-01 03:05:27.729 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:27.734 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:27.739 synch point, and intraline difference marking is done on the
2025-07-01 03:05:27.743 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:27.748
2025-07-01 03:05:27.752 Example:
2025-07-01 03:05:27.757
2025-07-01 03:05:27.761 >>> d = Differ()
2025-07-01 03:05:27.765 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:27.770 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:27.776 >>> print(''.join(results), end="")
2025-07-01 03:05:27.781 - abcDefghiJkl
2025-07-01 03:05:27.790 + abcdefGhijkl
2025-07-01 03:05:27.799 """
2025-07-01 03:05:27.803
2025-07-01 03:05:27.808 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:27.812 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:27.816 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:27.821 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:27.825 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:27.830
2025-07-01 03:05:27.834 # search for the pair that matches best without being identical
2025-07-01 03:05:27.838 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:27.843 # on junk -- unless we have to)
2025-07-01 03:05:27.847 for j in range(blo, bhi):
2025-07-01 03:05:27.851 bj = b[j]
2025-07-01 03:05:27.856 cruncher.set_seq2(bj)
2025-07-01 03:05:27.860 for i in range(alo, ahi):
2025-07-01 03:05:27.864 ai = a[i]
2025-07-01 03:05:27.869 if ai == bj:
2025-07-01 03:05:27.873 if eqi is None:
2025-07-01 03:05:27.877 eqi, eqj = i, j
2025-07-01 03:05:27.882 continue
2025-07-01 03:05:27.886 cruncher.set_seq1(ai)
2025-07-01 03:05:27.890 # computing similarity is expensive, so use the quick
2025-07-01 03:05:27.895 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:27.899 # compares by a factor of 3.
2025-07-01 03:05:27.903 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:27.908 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:27.913 # of the computation is cached by cruncher
2025-07-01 03:05:27.917 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:27.922 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:27.927 cruncher.ratio() > best_ratio:
2025-07-01 03:05:27.931 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:27.936 if best_ratio < cutoff:
2025-07-01 03:05:27.941 # no non-identical "pretty close" pair
2025-07-01 03:05:27.946 if eqi is None:
2025-07-01 03:05:27.950 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:27.955 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:27.959 return
2025-07-01 03:05:27.964 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:27.968 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:27.973 else:
2025-07-01 03:05:27.977 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:27.981 eqi = None
2025-07-01 03:05:27.985
2025-07-01 03:05:27.990 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:27.995 # identical
2025-07-01 03:05:27.999
2025-07-01 03:05:28.005 # pump out diffs from before the synch point
2025-07-01 03:05:28.010 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:28.014
2025-07-01 03:05:28.019 # do intraline marking on the synch pair
2025-07-01 03:05:28.023 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:28.028 if eqi is None:
2025-07-01 03:05:28.032 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:28.036 atags = btags = ""
2025-07-01 03:05:28.041 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:28.045 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:28.050 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:28.054 if tag == 'replace':
2025-07-01 03:05:28.058 atags += '^' * la
2025-07-01 03:05:28.063 btags += '^' * lb
2025-07-01 03:05:28.067 elif tag == 'delete':
2025-07-01 03:05:28.071 atags += '-' * la
2025-07-01 03:05:28.077 elif tag == 'insert':
2025-07-01 03:05:28.082 btags += '+' * lb
2025-07-01 03:05:28.086 elif tag == 'equal':
2025-07-01 03:05:28.090 atags += ' ' * la
2025-07-01 03:05:28.095 btags += ' ' * lb
2025-07-01 03:05:28.099 else:
2025-07-01 03:05:28.103 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:28.108 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:28.112 else:
2025-07-01 03:05:28.117 # the synch pair is identical
2025-07-01 03:05:28.121 yield '  ' + aelt
2025-07-01 03:05:28.125
2025-07-01 03:05:28.129 # pump out diffs from after the synch point
2025-07-01 03:05:28.134 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:28.138
2025-07-01 03:05:28.143 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:28.147 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:28.151
2025-07-01 03:05:28.156 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:28.160 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:28.165 alo = 450, ahi = 1101
2025-07-01 03:05:28.170 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:28.174 blo = 450, bhi = 1101
2025-07-01 03:05:28.178
2025-07-01 03:05:28.182 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:28.187 g = []
2025-07-01 03:05:28.191 if alo < ahi:
2025-07-01 03:05:28.195 if blo < bhi:
2025-07-01 03:05:28.200 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:28.204 else:
2025-07-01 03:05:28.208 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:28.213 elif blo < bhi:
2025-07-01 03:05:28.217 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:28.223
2025-07-01 03:05:28.228 >       yield from g
2025-07-01 03:05:28.235
2025-07-01 03:05:28.241 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:28.247 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:28.254
2025-07-01 03:05:28.260 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:28.266 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:28.270 alo = 450, ahi = 1101
2025-07-01 03:05:28.275 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:28.279 blo = 450, bhi = 1101
2025-07-01 03:05:28.283
2025-07-01 03:05:28.288 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:28.292 r"""
2025-07-01 03:05:28.297 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:28.301 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:28.305 synch point, and intraline difference marking is done on the
2025-07-01 03:05:28.310 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:28.314
2025-07-01 03:05:28.319 Example:
2025-07-01 03:05:28.323
2025-07-01 03:05:28.328 >>> d = Differ()
2025-07-01 03:05:28.332 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:28.337 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:28.341 >>> print(''.join(results), end="")
2025-07-01 03:05:28.346 - abcDefghiJkl
2025-07-01 03:05:28.354 + abcdefGhijkl
2025-07-01 03:05:28.363 """
2025-07-01 03:05:28.368
2025-07-01 03:05:28.372 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:28.377 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:28.382 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:28.386 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:28.391 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:28.395
2025-07-01 03:05:28.400 # search for the pair that matches best without being identical
2025-07-01 03:05:28.405 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:28.409 # on junk -- unless we have to)
2025-07-01 03:05:28.413 for j in range(blo, bhi):
2025-07-01 03:05:28.418 bj = b[j]
2025-07-01 03:05:28.422 cruncher.set_seq2(bj)
2025-07-01 03:05:28.427 for i in range(alo, ahi):
2025-07-01 03:05:28.431 ai = a[i]
2025-07-01 03:05:28.437 if ai == bj:
2025-07-01 03:05:28.441 if eqi is None:
2025-07-01 03:05:28.446 eqi, eqj = i, j
2025-07-01 03:05:28.450 continue
2025-07-01 03:05:28.454 cruncher.set_seq1(ai)
2025-07-01 03:05:28.459 # computing similarity is expensive, so use the quick
2025-07-01 03:05:28.464 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:28.468 # compares by a factor of 3.
2025-07-01 03:05:28.473 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:28.478 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:28.484 # of the computation is cached by cruncher
2025-07-01 03:05:28.490 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:28.495 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:28.499 cruncher.ratio() > best_ratio:
2025-07-01 03:05:28.504 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:28.508 if best_ratio < cutoff:
2025-07-01 03:05:28.513 # no non-identical "pretty close" pair
2025-07-01 03:05:28.518 if eqi is None:
2025-07-01 03:05:28.523 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:28.528 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:28.533 return
2025-07-01 03:05:28.538 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:28.544 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:28.550 else:
2025-07-01 03:05:28.555 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:28.560 eqi = None
2025-07-01 03:05:28.566
2025-07-01 03:05:28.570 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:28.575 # identical
2025-07-01 03:05:28.580
2025-07-01 03:05:28.585 # pump out diffs from before the synch point
2025-07-01 03:05:28.589 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:28.594
2025-07-01 03:05:28.599 # do intraline marking on the synch pair
2025-07-01 03:05:28.603 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:28.608 if eqi is None:
2025-07-01 03:05:28.613 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:28.618 atags = btags = ""
2025-07-01 03:05:28.624 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:28.628 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:28.633 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:28.637 if tag == 'replace':
2025-07-01 03:05:28.642 atags += '^' * la
2025-07-01 03:05:28.646 btags += '^' * lb
2025-07-01 03:05:28.651 elif tag == 'delete':
2025-07-01 03:05:28.656 atags += '-' * la
2025-07-01 03:05:28.661 elif tag == 'insert':
2025-07-01 03:05:28.666 btags += '+' * lb
2025-07-01 03:05:28.670 elif tag == 'equal':
2025-07-01 03:05:28.675 atags += ' ' * la
2025-07-01 03:05:28.679 btags += ' ' * lb
2025-07-01 03:05:28.683 else:
2025-07-01 03:05:28.688 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:28.693 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:28.697 else:
2025-07-01 03:05:28.702 # the synch pair is identical
2025-07-01 03:05:28.706 yield '  ' + aelt
2025-07-01 03:05:28.711
2025-07-01 03:05:28.715 # pump out diffs from after the synch point
2025-07-01 03:05:28.720 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:28.724
2025-07-01 03:05:28.729 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:28.734 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:28.738
2025-07-01 03:05:28.743 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:28.747 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:28.752 alo = 451, ahi = 1101
2025-07-01 03:05:28.757 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:28.762 blo = 451, bhi = 1101
2025-07-01 03:05:28.767
2025-07-01 03:05:28.771 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:28.776 g = []
2025-07-01 03:05:28.780 if alo < ahi:
2025-07-01 03:05:28.784 if blo < bhi:
2025-07-01 03:05:28.789 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:28.793 else:
2025-07-01 03:05:28.798 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:28.802 elif blo < bhi:
2025-07-01 03:05:28.807 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:28.812
2025-07-01 03:05:28.817 >       yield from g
2025-07-01 03:05:28.821
2025-07-01 03:05:28.826 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:28.830 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:28.834
2025-07-01 03:05:28.839 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:28.843 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:28.848 alo = 451, ahi = 1101
2025-07-01 03:05:28.852 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:28.857 blo = 451, bhi = 1101
2025-07-01 03:05:28.862
2025-07-01 03:05:28.867 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:28.871 r"""
2025-07-01 03:05:28.876 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:28.880 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:28.885 synch point, and intraline difference marking is done on the
2025-07-01 03:05:28.889 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:28.894
2025-07-01 03:05:28.898 Example:
2025-07-01 03:05:28.902
2025-07-01 03:05:28.907 >>> d = Differ()
2025-07-01 03:05:28.911 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:28.915 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:28.920 >>> print(''.join(results), end="")
2025-07-01 03:05:28.924 - abcDefghiJkl
2025-07-01 03:05:28.934 + abcdefGhijkl
2025-07-01 03:05:28.942 """
2025-07-01 03:05:28.946
2025-07-01 03:05:28.951 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:28.955 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:28.959 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:28.964 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:28.969 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:28.973
2025-07-01 03:05:28.978 # search for the pair that matches best without being identical
2025-07-01 03:05:28.982 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:28.986 # on junk -- unless we have to)
2025-07-01 03:05:28.991 for j in range(blo, bhi):
2025-07-01 03:05:28.995 bj = b[j]
2025-07-01 03:05:29.001 cruncher.set_seq2(bj)
2025-07-01 03:05:29.005 for i in range(alo, ahi):
2025-07-01 03:05:29.010 ai = a[i]
2025-07-01 03:05:29.014 if ai == bj:
2025-07-01 03:05:29.018 if eqi is None:
2025-07-01 03:05:29.022 eqi, eqj = i, j
2025-07-01 03:05:29.027 continue
2025-07-01 03:05:29.031 cruncher.set_seq1(ai)
2025-07-01 03:05:29.035 # computing similarity is expensive, so use the quick
2025-07-01 03:05:29.040 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:29.044 # compares by a factor of 3.
2025-07-01 03:05:29.049 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:29.053 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:29.058 # of the computation is cached by cruncher
2025-07-01 03:05:29.062 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:29.067 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:29.072 cruncher.ratio() > best_ratio:
2025-07-01 03:05:29.077 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:29.081 if best_ratio < cutoff:
2025-07-01 03:05:29.086 # no non-identical "pretty close" pair
2025-07-01 03:05:29.090 if eqi is None:
2025-07-01 03:05:29.095 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:29.099 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:29.104 return
2025-07-01 03:05:29.108 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:29.113 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:29.117 else:
2025-07-01 03:05:29.121 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:29.126 eqi = None
2025-07-01 03:05:29.130
2025-07-01 03:05:29.135 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:29.139 # identical
2025-07-01 03:05:29.144
2025-07-01 03:05:29.148 # pump out diffs from before the synch point
2025-07-01 03:05:29.153 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:29.158
2025-07-01 03:05:29.162 # do intraline marking on the synch pair
2025-07-01 03:05:29.167 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:29.171 if eqi is None:
2025-07-01 03:05:29.176 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:29.181 atags = btags = ""
2025-07-01 03:05:29.185 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:29.190 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:29.194 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:29.198 if tag == 'replace':
2025-07-01 03:05:29.203 atags += '^' * la
2025-07-01 03:05:29.207 btags += '^' * lb
2025-07-01 03:05:29.211 elif tag == 'delete':
2025-07-01 03:05:29.216 atags += '-' * la
2025-07-01 03:05:29.221 elif tag == 'insert':
2025-07-01 03:05:29.225 btags += '+' * lb
2025-07-01 03:05:29.229 elif tag == 'equal':
2025-07-01 03:05:29.234 atags += ' ' * la
2025-07-01 03:05:29.238 btags += ' ' * lb
2025-07-01 03:05:29.242 else:
2025-07-01 03:05:29.247 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:29.251 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:29.256 else:
2025-07-01 03:05:29.260 # the synch pair is identical
2025-07-01 03:05:29.264 yield '  ' + aelt
2025-07-01 03:05:29.269
2025-07-01 03:05:29.273 # pump out diffs from after the synch point
2025-07-01 03:05:29.278 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:29.283
2025-07-01 03:05:29.288 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:29.292 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:29.297
2025-07-01 03:05:29.301 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:29.306 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:29.310 alo = 452, ahi = 1101
2025-07-01 03:05:29.315 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:29.319 blo = 452, bhi = 1101
2025-07-01 03:05:29.324
2025-07-01 03:05:29.328 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:29.333 g = []
2025-07-01 03:05:29.337 if alo < ahi:
2025-07-01 03:05:29.341 if blo < bhi:
2025-07-01 03:05:29.346 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:29.351 else:
2025-07-01 03:05:29.355 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:29.359 elif blo < bhi:
2025-07-01 03:05:29.364 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:29.368
2025-07-01 03:05:29.372 >       yield from g
2025-07-01 03:05:29.377
2025-07-01 03:05:29.382 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:29.387 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:29.392
2025-07-01 03:05:29.396 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:29.401 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:29.406 alo = 452, ahi = 1101
2025-07-01 03:05:29.411 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:29.415 blo = 452, bhi = 1101
2025-07-01 03:05:29.420
2025-07-01 03:05:29.425 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:29.429 r"""
2025-07-01 03:05:29.434 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:29.438 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:29.443 synch point, and intraline difference marking is done on the
2025-07-01 03:05:29.447 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:29.452
2025-07-01 03:05:29.456 Example:
2025-07-01 03:05:29.460
2025-07-01 03:05:29.465 >>> d = Differ()
2025-07-01 03:05:29.469 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:29.474 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:29.478 >>> print(''.join(results), end="")
2025-07-01 03:05:29.483 - abcDefghiJkl
2025-07-01 03:05:29.492 + abcdefGhijkl
2025-07-01 03:05:29.501 """
2025-07-01 03:05:29.505
2025-07-01 03:05:29.510 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:29.515 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:29.519 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:29.523 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:29.528 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:29.532
2025-07-01 03:05:29.537 # search for the pair that matches best without being identical
2025-07-01 03:05:29.541 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:29.545 # on junk -- unless we have to)
2025-07-01 03:05:29.550 for j in range(blo, bhi):
2025-07-01 03:05:29.554 bj = b[j]
2025-07-01 03:05:29.558 cruncher.set_seq2(bj)
2025-07-01 03:05:29.563 for i in range(alo, ahi):
2025-07-01 03:05:29.567 ai = a[i]
2025-07-01 03:05:29.571 if ai == bj:
2025-07-01 03:05:29.576 if eqi is None:
2025-07-01 03:05:29.580 eqi, eqj = i, j
2025-07-01 03:05:29.585 continue
2025-07-01 03:05:29.589 cruncher.set_seq1(ai)
2025-07-01 03:05:29.594 # computing similarity is expensive, so use the quick
2025-07-01 03:05:29.599 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:29.603 # compares by a factor of 3.
2025-07-01 03:05:29.607 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:29.612 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:29.616 # of the computation is cached by cruncher
2025-07-01 03:05:29.620 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:29.625 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:29.629 cruncher.ratio() > best_ratio:
2025-07-01 03:05:29.634 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:29.638 if best_ratio < cutoff:
2025-07-01 03:05:29.642 # no non-identical "pretty close" pair
2025-07-01 03:05:29.649 if eqi is None:
2025-07-01 03:05:29.654 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:29.658 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:29.663 return
2025-07-01 03:05:29.667 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:29.672 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:29.676 else:
2025-07-01 03:05:29.681 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:29.685 eqi = None
2025-07-01 03:05:29.690
2025-07-01 03:05:29.695 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:29.700 # identical
2025-07-01 03:05:29.705
2025-07-01 03:05:29.709 # pump out diffs from before the synch point
2025-07-01 03:05:29.714 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:29.718
2025-07-01 03:05:29.722 # do intraline marking on the synch pair
2025-07-01 03:05:29.727 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:29.731 if eqi is None:
2025-07-01 03:05:29.736 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:29.740 atags = btags = ""
2025-07-01 03:05:29.744 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:29.749 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:29.753 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:29.758 if tag == 'replace':
2025-07-01 03:05:29.762 atags += '^' * la
2025-07-01 03:05:29.767 btags += '^' * lb
2025-07-01 03:05:29.771 elif tag == 'delete':
2025-07-01 03:05:29.775 atags += '-' * la
2025-07-01 03:05:29.780 elif tag == 'insert':
2025-07-01 03:05:29.785 btags += '+' * lb
2025-07-01 03:05:29.791 elif tag == 'equal':
2025-07-01 03:05:29.797 atags += ' ' * la
2025-07-01 03:05:29.803 btags += ' ' * lb
2025-07-01 03:05:29.809 else:
2025-07-01 03:05:29.814 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:29.821 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:29.827 else:
2025-07-01 03:05:29.832 # the synch pair is identical
2025-07-01 03:05:29.837 yield '  ' + aelt
2025-07-01 03:05:29.841
2025-07-01 03:05:29.847 # pump out diffs from after the synch point
2025-07-01 03:05:29.853 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:29.858
2025-07-01 03:05:29.863 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:29.869 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:29.874
2025-07-01 03:05:29.880 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:29.886 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:29.892 alo = 453, ahi = 1101
2025-07-01 03:05:29.899 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:29.904 blo = 453, bhi = 1101
2025-07-01 03:05:29.908
2025-07-01 03:05:29.912 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:29.917 g = []
2025-07-01 03:05:29.921 if alo < ahi:
2025-07-01 03:05:29.925 if blo < bhi:
2025-07-01 03:05:29.930 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:29.934 else:
2025-07-01 03:05:29.938 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:29.942 elif blo < bhi:
2025-07-01 03:05:29.947 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:29.951
2025-07-01 03:05:29.955 >       yield from g
2025-07-01 03:05:29.960
2025-07-01 03:05:29.964 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:29.968 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:29.973
2025-07-01 03:05:29.977 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:29.981 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:29.986 alo = 453, ahi = 1101
2025-07-01 03:05:29.990 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:29.994 blo = 453, bhi = 1101
2025-07-01 03:05:29.999
2025-07-01 03:05:30.003 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:30.008 r"""
2025-07-01 03:05:30.012 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:30.018 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:30.023 synch point, and intraline difference marking is done on the
2025-07-01 03:05:30.027 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:30.032
2025-07-01 03:05:30.036 Example:
2025-07-01 03:05:30.041
2025-07-01 03:05:30.045 >>> d = Differ()
2025-07-01 03:05:30.049 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:30.053 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:30.058 >>> print(''.join(results), end="")
2025-07-01 03:05:30.062 - abcDefghiJkl
2025-07-01 03:05:30.071 + abcdefGhijkl
2025-07-01 03:05:30.080 """
2025-07-01 03:05:30.084
2025-07-01 03:05:30.088 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:30.093 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:30.097 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:30.102 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:30.106 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:30.110
2025-07-01 03:05:30.115 # search for the pair that matches best without being identical
2025-07-01 03:05:30.119 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:30.123 # on junk -- unless we have to)
2025-07-01 03:05:30.128 for j in range(blo, bhi):
2025-07-01 03:05:30.132 bj = b[j]
2025-07-01 03:05:30.136 cruncher.set_seq2(bj)
2025-07-01 03:05:30.141 for i in range(alo, ahi):
2025-07-01 03:05:30.145 ai = a[i]
2025-07-01 03:05:30.149 if ai == bj:
2025-07-01 03:05:30.154 if eqi is None:
2025-07-01 03:05:30.158 eqi, eqj = i, j
2025-07-01 03:05:30.163 continue
2025-07-01 03:05:30.167 cruncher.set_seq1(ai)
2025-07-01 03:05:30.172 # computing similarity is expensive, so use the quick
2025-07-01 03:05:30.177 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:30.182 # compares by a factor of 3.
2025-07-01 03:05:30.186 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:30.190 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:30.195 # of the computation is cached by cruncher
2025-07-01 03:05:30.200 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:30.204 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:30.209 cruncher.ratio() > best_ratio:
2025-07-01 03:05:30.213 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:30.218 if best_ratio < cutoff:
2025-07-01 03:05:30.222 # no non-identical "pretty close" pair
2025-07-01 03:05:30.226 if eqi is None:
2025-07-01 03:05:30.231 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:30.235 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:30.240 return
2025-07-01 03:05:30.244 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:30.249 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:30.253 else:
2025-07-01 03:05:30.257 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:30.262 eqi = None
2025-07-01 03:05:30.266
2025-07-01 03:05:30.271 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:30.276 # identical
2025-07-01 03:05:30.280
2025-07-01 03:05:30.284 # pump out diffs from before the synch point
2025-07-01 03:05:30.289 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:30.293
2025-07-01 03:05:30.297 # do intraline marking on the synch pair
2025-07-01 03:05:30.302 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:30.306 if eqi is None:
2025-07-01 03:05:30.310 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:30.315 atags = btags = ""
2025-07-01 03:05:30.319 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:30.324 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:30.328 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:30.332 if tag == 'replace':
2025-07-01 03:05:30.337 atags += '^' * la
2025-07-01 03:05:30.341 btags += '^' * lb
2025-07-01 03:05:30.346 elif tag == 'delete':
2025-07-01 03:05:30.351 atags += '-' * la
2025-07-01 03:05:30.355 elif tag == 'insert':
2025-07-01 03:05:30.360 btags += '+' * lb
2025-07-01 03:05:30.364 elif tag == 'equal':
2025-07-01 03:05:30.369 atags += ' ' * la
2025-07-01 03:05:30.374 btags += ' ' * lb
2025-07-01 03:05:30.379 else:
2025-07-01 03:05:30.383 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:30.388 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:30.392 else:
2025-07-01 03:05:30.397 # the synch pair is identical
2025-07-01 03:05:30.401 yield '  ' + aelt
2025-07-01 03:05:30.406
2025-07-01 03:05:30.411 # pump out diffs from after the synch point
2025-07-01 03:05:30.416 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:30.420
2025-07-01 03:05:30.425 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:30.430 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:30.434
2025-07-01 03:05:30.439 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:30.444 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:30.448 alo = 454, ahi = 1101
2025-07-01 03:05:30.454 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:30.458 blo = 454, bhi = 1101
2025-07-01 03:05:30.463
2025-07-01 03:05:30.468 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:30.472 g = []
2025-07-01 03:05:30.477 if alo < ahi:
2025-07-01 03:05:30.482 if blo < bhi:
2025-07-01 03:05:30.487 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:30.492 else:
2025-07-01 03:05:30.496 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:30.501 elif blo < bhi:
2025-07-01 03:05:30.505 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:30.510
2025-07-01 03:05:30.514 >       yield from g
2025-07-01 03:05:30.518
2025-07-01 03:05:30.523 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:30.528 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:30.532
2025-07-01 03:05:30.537 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:30.542 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:30.546 alo = 454, ahi = 1101
2025-07-01 03:05:30.551 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:30.556 blo = 454, bhi = 1101
2025-07-01 03:05:30.560
2025-07-01 03:05:30.565 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:30.569 r"""
2025-07-01 03:05:30.573 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:30.578 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:30.583 synch point, and intraline difference marking is done on the
2025-07-01 03:05:30.587 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:30.591
2025-07-01 03:05:30.596 Example:
2025-07-01 03:05:30.600
2025-07-01 03:05:30.605 >>> d = Differ()
2025-07-01 03:05:30.609 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:30.614 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:30.618 >>> print(''.join(results), end="")
2025-07-01 03:05:30.623 - abcDefghiJkl
2025-07-01 03:05:30.631 + abcdefGhijkl
2025-07-01 03:05:30.640 """
2025-07-01 03:05:30.644
2025-07-01 03:05:30.649 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:30.653 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:30.658 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:30.662 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:30.667 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:30.671
2025-07-01 03:05:30.675 # search for the pair that matches best without being identical
2025-07-01 03:05:30.680 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:30.685 # on junk -- unless we have to)
2025-07-01 03:05:30.689 for j in range(blo, bhi):
2025-07-01 03:05:30.694 bj = b[j]
2025-07-01 03:05:30.698 cruncher.set_seq2(bj)
2025-07-01 03:05:30.702 for i in range(alo, ahi):
2025-07-01 03:05:30.707 ai = a[i]
2025-07-01 03:05:30.711 if ai == bj:
2025-07-01 03:05:30.715 if eqi is None:
2025-07-01 03:05:30.720 eqi, eqj = i, j
2025-07-01 03:05:30.725 continue
2025-07-01 03:05:30.730 cruncher.set_seq1(ai)
2025-07-01 03:05:30.735 # computing similarity is expensive, so use the quick
2025-07-01 03:05:30.740 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:30.745 # compares by a factor of 3.
2025-07-01 03:05:30.750 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:30.755 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:30.759 # of the computation is cached by cruncher
2025-07-01 03:05:30.764 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:30.768 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:30.773 cruncher.ratio() > best_ratio:
2025-07-01 03:05:30.777 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:30.781 if best_ratio < cutoff:
2025-07-01 03:05:30.786 # no non-identical "pretty close" pair
2025-07-01 03:05:30.790 if eqi is None:
2025-07-01 03:05:30.795 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:30.799 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:30.804 return
2025-07-01 03:05:30.808 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:30.813 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:30.817 else:
2025-07-01 03:05:30.822 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:30.826 eqi = None
2025-07-01 03:05:30.830
2025-07-01 03:05:30.836 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:30.841 # identical
2025-07-01 03:05:30.845
2025-07-01 03:05:30.854 # pump out diffs from before the synch point
2025-07-01 03:05:30.866 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:30.871
2025-07-01 03:05:30.876 # do intraline marking on the synch pair
2025-07-01 03:05:30.880 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:30.885 if eqi is None:
2025-07-01 03:05:30.889 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:30.894 atags = btags = ""
2025-07-01 03:05:30.898 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:30.903 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:30.908 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:30.912 if tag == 'replace':
2025-07-01 03:05:30.917 atags += '^' * la
2025-07-01 03:05:30.921 btags += '^' * lb
2025-07-01 03:05:30.926 elif tag == 'delete':
2025-07-01 03:05:30.930 atags += '-' * la
2025-07-01 03:05:30.935 elif tag == 'insert':
2025-07-01 03:05:30.939 btags += '+' * lb
2025-07-01 03:05:30.944 elif tag == 'equal':
2025-07-01 03:05:30.949 atags += ' ' * la
2025-07-01 03:05:30.953 btags += ' ' * lb
2025-07-01 03:05:30.957 else:
2025-07-01 03:05:30.962 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:30.966 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:30.971 else:
2025-07-01 03:05:30.975 # the synch pair is identical
2025-07-01 03:05:30.979 yield '  ' + aelt
2025-07-01 03:05:30.984
2025-07-01 03:05:30.990 # pump out diffs from after the synch point
2025-07-01 03:05:30.994 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:30.998
2025-07-01 03:05:31.003 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:31.008 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:31.013
2025-07-01 03:05:31.017 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:31.025 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:31.031 alo = 455, ahi = 1101
2025-07-01 03:05:31.039 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:31.044 blo = 455, bhi = 1101
2025-07-01 03:05:31.048
2025-07-01 03:05:31.053 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:31.057 g = []
2025-07-01 03:05:31.062 if alo < ahi:
2025-07-01 03:05:31.068 if blo < bhi:
2025-07-01 03:05:31.072 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:31.077 else:
2025-07-01 03:05:31.082 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:31.086 elif blo < bhi:
2025-07-01 03:05:31.091 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:31.096
2025-07-01 03:05:31.101 >       yield from g
2025-07-01 03:05:31.105
2025-07-01 03:05:31.109 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:31.114 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:31.118
2025-07-01 03:05:31.123 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:31.127 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:31.131 alo = 455, ahi = 1101
2025-07-01 03:05:31.136 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:31.142 blo = 455, bhi = 1101
2025-07-01 03:05:31.148
2025-07-01 03:05:31.155 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:31.161 r"""
2025-07-01 03:05:31.168 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:31.175 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:31.182 synch point, and intraline difference marking is done on the
2025-07-01 03:05:31.188 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:31.193
2025-07-01 03:05:31.198 Example:
2025-07-01 03:05:31.202
2025-07-01 03:05:31.207 >>> d = Differ()
2025-07-01 03:05:31.211 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:31.216 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:31.220 >>> print(''.join(results), end="")
2025-07-01 03:05:31.225 - abcDefghiJkl
2025-07-01 03:05:31.234 + abcdefGhijkl
2025-07-01 03:05:31.243 """
2025-07-01 03:05:31.247
2025-07-01 03:05:31.251 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:31.256 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:31.261 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:31.265 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:31.269 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:31.274
2025-07-01 03:05:31.278 # search for the pair that matches best without being identical
2025-07-01 03:05:31.283 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:31.287 # on junk -- unless we have to)
2025-07-01 03:05:31.292 for j in range(blo, bhi):
2025-07-01 03:05:31.297 bj = b[j]
2025-07-01 03:05:31.301 cruncher.set_seq2(bj)
2025-07-01 03:05:31.306 for i in range(alo, ahi):
2025-07-01 03:05:31.310 ai = a[i]
2025-07-01 03:05:31.314 if ai == bj:
2025-07-01 03:05:31.319 if eqi is None:
2025-07-01 03:05:31.323 eqi, eqj = i, j
2025-07-01 03:05:31.328 continue
2025-07-01 03:05:31.333 cruncher.set_seq1(ai)
2025-07-01 03:05:31.337 # computing similarity is expensive, so use the quick
2025-07-01 03:05:31.342 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:31.346 # compares by a factor of 3.
2025-07-01 03:05:31.351 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:31.355 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:31.360 # of the computation is cached by cruncher
2025-07-01 03:05:31.364 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:31.369 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:31.374 cruncher.ratio() > best_ratio:
2025-07-01 03:05:31.378 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:31.383 if best_ratio < cutoff:
2025-07-01 03:05:31.388 # no non-identical "pretty close" pair
2025-07-01 03:05:31.392 if eqi is None:
2025-07-01 03:05:31.397 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:31.401 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:31.406 return
2025-07-01 03:05:31.410 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:31.415 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:31.420 else:
2025-07-01 03:05:31.424 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:31.428 eqi = None
2025-07-01 03:05:31.433
2025-07-01 03:05:31.437 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:31.442 # identical
2025-07-01 03:05:31.446
2025-07-01 03:05:31.450 # pump out diffs from before the synch point
2025-07-01 03:05:31.455 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:31.459
2025-07-01 03:05:31.463 # do intraline marking on the synch pair
2025-07-01 03:05:31.468 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:31.472 if eqi is None:
2025-07-01 03:05:31.477 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:31.481 atags = btags = ""
2025-07-01 03:05:31.485 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:31.490 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:31.494 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:31.500 if tag == 'replace':
2025-07-01 03:05:31.505 atags += '^' * la
2025-07-01 03:05:31.509 btags += '^' * lb
2025-07-01 03:05:31.514 elif tag == 'delete':
2025-07-01 03:05:31.519 atags += '-' * la
2025-07-01 03:05:31.524 elif tag == 'insert':
2025-07-01 03:05:31.529 btags += '+' * lb
2025-07-01 03:05:31.534 elif tag == 'equal':
2025-07-01 03:05:31.538 atags += ' ' * la
2025-07-01 03:05:31.543 btags += ' ' * lb
2025-07-01 03:05:31.547 else:
2025-07-01 03:05:31.552 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:31.557 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:31.561 else:
2025-07-01 03:05:31.566 # the synch pair is identical
2025-07-01 03:05:31.570 yield '  ' + aelt
2025-07-01 03:05:31.575
2025-07-01 03:05:31.579 # pump out diffs from after the synch point
2025-07-01 03:05:31.584 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:31.589
2025-07-01 03:05:31.593 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:31.598 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:31.602
2025-07-01 03:05:31.607 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:31.611 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:31.616 alo = 456, ahi = 1101
2025-07-01 03:05:31.621 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:31.625 blo = 456, bhi = 1101
2025-07-01 03:05:31.630
2025-07-01 03:05:31.634 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:31.639 g = []
2025-07-01 03:05:31.643 if alo < ahi:
2025-07-01 03:05:31.647 if blo < bhi:
2025-07-01 03:05:31.652 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:31.656 else:
2025-07-01 03:05:31.661 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:31.665 elif blo < bhi:
2025-07-01 03:05:31.669 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:31.674
2025-07-01 03:05:31.678 >       yield from g
2025-07-01 03:05:31.683
2025-07-01 03:05:31.687 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:31.692 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:31.697
2025-07-01 03:05:31.701 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:31.706 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:31.711 alo = 456, ahi = 1101
2025-07-01 03:05:31.716 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:31.720 blo = 456, bhi = 1101
2025-07-01 03:05:31.725
2025-07-01 03:05:31.729 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:31.734 r"""
2025-07-01 03:05:31.738 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:31.743 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:31.747 synch point, and intraline difference marking is done on the
2025-07-01 03:05:31.752 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:31.756
2025-07-01 03:05:31.760 Example:
2025-07-01 03:05:31.765
2025-07-01 03:05:31.769 >>> d = Differ()
2025-07-01 03:05:31.774 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:31.778 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:31.783 >>> print(''.join(results), end="")
2025-07-01 03:05:31.788 - abcDefghiJkl
2025-07-01 03:05:31.796 + abcdefGhijkl
2025-07-01 03:05:31.805 """
2025-07-01 03:05:31.809
2025-07-01 03:05:31.814 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:31.819 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:31.823 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:31.828 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:31.832 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:31.837
2025-07-01 03:05:31.841 # search for the pair that matches best without being identical
2025-07-01 03:05:31.845 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:31.850 # on junk -- unless we have to)
2025-07-01 03:05:31.854 for j in range(blo, bhi):
2025-07-01 03:05:31.858 bj = b[j]
2025-07-01 03:05:31.862 cruncher.set_seq2(bj)
2025-07-01 03:05:31.867 for i in range(alo, ahi):
2025-07-01 03:05:31.871 ai = a[i]
2025-07-01 03:05:31.875 if ai == bj:
2025-07-01 03:05:31.880 if eqi is None:
2025-07-01 03:05:31.884 eqi, eqj = i, j
2025-07-01 03:05:31.889 continue
2025-07-01 03:05:31.894 cruncher.set_seq1(ai)
2025-07-01 03:05:31.899 # computing similarity is expensive, so use the quick
2025-07-01 03:05:31.904 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:31.908 # compares by a factor of 3.
2025-07-01 03:05:31.912 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:31.917 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:31.922 # of the computation is cached by cruncher
2025-07-01 03:05:31.927 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:31.931 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:31.936 cruncher.ratio() > best_ratio:
2025-07-01 03:05:31.940 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:31.945 if best_ratio < cutoff:
2025-07-01 03:05:31.949 # no non-identical "pretty close" pair
2025-07-01 03:05:31.954 if eqi is None:
2025-07-01 03:05:31.958 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:31.963 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:31.967 return
2025-07-01 03:05:31.972 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:31.976 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:31.981 else:
2025-07-01 03:05:31.985 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:31.989 eqi = None
2025-07-01 03:05:31.994
2025-07-01 03:05:31.998 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:32.003 # identical
2025-07-01 03:05:32.007
2025-07-01 03:05:32.012 # pump out diffs from before the synch point
2025-07-01 03:05:32.017 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:32.021
2025-07-01 03:05:32.025 # do intraline marking on the synch pair
2025-07-01 03:05:32.030 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:32.034 if eqi is None:
2025-07-01 03:05:32.039 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:32.044 atags = btags = ""
2025-07-01 03:05:32.048 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:32.053 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:32.057 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:32.062 if tag == 'replace':
2025-07-01 03:05:32.066 atags += '^' * la
2025-07-01 03:05:32.071 btags += '^' * lb
2025-07-01 03:05:32.075 elif tag == 'delete':
2025-07-01 03:05:32.079 atags += '-' * la
2025-07-01 03:05:32.084 elif tag == 'insert':
2025-07-01 03:05:32.088 btags += '+' * lb
2025-07-01 03:05:32.093 elif tag == 'equal':
2025-07-01 03:05:32.097 atags += ' ' * la
2025-07-01 03:05:32.102 btags += ' ' * lb
2025-07-01 03:05:32.106 else:
2025-07-01 03:05:32.111 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:32.115 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:32.120 else:
2025-07-01 03:05:32.124 # the synch pair is identical
2025-07-01 03:05:32.129 yield '  ' + aelt
2025-07-01 03:05:32.133
2025-07-01 03:05:32.137 # pump out diffs from after the synch point
2025-07-01 03:05:32.142 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:32.146
2025-07-01 03:05:32.151 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:32.155 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:32.159
2025-07-01 03:05:32.164 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:32.169 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:32.173 alo = 457, ahi = 1101
2025-07-01 03:05:32.178 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:32.183 blo = 457, bhi = 1101
2025-07-01 03:05:32.187
2025-07-01 03:05:32.192 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:32.197 g = []
2025-07-01 03:05:32.201 if alo < ahi:
2025-07-01 03:05:32.206 if blo < bhi:
2025-07-01 03:05:32.210 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:32.215 else:
2025-07-01 03:05:32.220 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:32.224 elif blo < bhi:
2025-07-01 03:05:32.228 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:32.233
2025-07-01 03:05:32.237 >       yield from g
2025-07-01 03:05:32.241
2025-07-01 03:05:32.245 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:32.250 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:32.254
2025-07-01 03:05:32.258 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:32.263 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:32.268 alo = 457, ahi = 1101
2025-07-01 03:05:32.273 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:32.277 blo = 457, bhi = 1101
2025-07-01 03:05:32.281
2025-07-01 03:05:32.286 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:32.290 r"""
2025-07-01 03:05:32.295 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:32.300 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:32.305 synch point, and intraline difference marking is done on the
2025-07-01 03:05:32.309 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:32.314
2025-07-01 03:05:32.318 Example:
2025-07-01 03:05:32.322
2025-07-01 03:05:32.327 >>> d = Differ()
2025-07-01 03:05:32.331 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:32.336 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:32.340 >>> print(''.join(results), end="")
2025-07-01 03:05:32.344 - abcDefghiJkl
2025-07-01 03:05:32.353 + abcdefGhijkl
2025-07-01 03:05:32.362 """
2025-07-01 03:05:32.366
2025-07-01 03:05:32.371 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:32.375 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:32.380 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:32.384 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:32.389 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:32.393
2025-07-01 03:05:32.398 # search for the pair that matches best without being identical
2025-07-01 03:05:32.402 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:32.407 # on junk -- unless we have to)
2025-07-01 03:05:32.411 for j in range(blo, bhi):
2025-07-01 03:05:32.416 bj = b[j]
2025-07-01 03:05:32.420 cruncher.set_seq2(bj)
2025-07-01 03:05:32.424 for i in range(alo, ahi):
2025-07-01 03:05:32.429 ai = a[i]
2025-07-01 03:05:32.436 if ai == bj:
2025-07-01 03:05:32.442 if eqi is None:
2025-07-01 03:05:32.448 eqi, eqj = i, j
2025-07-01 03:05:32.454 continue
2025-07-01 03:05:32.459 cruncher.set_seq1(ai)
2025-07-01 03:05:32.464 # computing similarity is expensive, so use the quick
2025-07-01 03:05:32.468 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:32.473 # compares by a factor of 3.
2025-07-01 03:05:32.477 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:32.481 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:32.486 # of the computation is cached by cruncher
2025-07-01 03:05:32.490 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:32.495 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:32.499 cruncher.ratio() > best_ratio:
2025-07-01 03:05:32.503 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:32.508 if best_ratio < cutoff:
2025-07-01 03:05:32.512 # no non-identical "pretty close" pair
2025-07-01 03:05:32.517 if eqi is None:
2025-07-01 03:05:32.521 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:32.526 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:32.530 return
2025-07-01 03:05:32.534 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:32.539 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:32.543 else:
2025-07-01 03:05:32.548 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:32.552 eqi = None
2025-07-01 03:05:32.556
2025-07-01 03:05:32.561 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:32.565 # identical
2025-07-01 03:05:32.570
2025-07-01 03:05:32.574 # pump out diffs from before the synch point
2025-07-01 03:05:32.578 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:32.582
2025-07-01 03:05:32.587 # do intraline marking on the synch pair
2025-07-01 03:05:32.591 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:32.597 if eqi is None:
2025-07-01 03:05:32.601 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:32.605 atags = btags = ""
2025-07-01 03:05:32.610 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:32.615 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:32.619 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:32.624 if tag == 'replace':
2025-07-01 03:05:32.628 atags += '^' * la
2025-07-01 03:05:32.633 btags += '^' * lb
2025-07-01 03:05:32.637 elif tag == 'delete':
2025-07-01 03:05:32.641 atags += '-' * la
2025-07-01 03:05:32.646 elif tag == 'insert':
2025-07-01 03:05:32.650 btags += '+' * lb
2025-07-01 03:05:32.655 elif tag == 'equal':
2025-07-01 03:05:32.659 atags += ' ' * la
2025-07-01 03:05:32.664 btags += ' ' * lb
2025-07-01 03:05:32.668 else:
2025-07-01 03:05:32.674 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:32.680 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:32.685 else:
2025-07-01 03:05:32.690 # the synch pair is identical
2025-07-01 03:05:32.694 yield '  ' + aelt
2025-07-01 03:05:32.699
2025-07-01 03:05:32.704 # pump out diffs from after the synch point
2025-07-01 03:05:32.709 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:32.713
2025-07-01 03:05:32.718 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:32.722 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:32.727
2025-07-01 03:05:32.732 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:32.737 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:32.741 alo = 458, ahi = 1101
2025-07-01 03:05:32.746 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:32.750 blo = 458, bhi = 1101
2025-07-01 03:05:32.754
2025-07-01 03:05:32.759 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:32.763 g = []
2025-07-01 03:05:32.767 if alo < ahi:
2025-07-01 03:05:32.771 if blo < bhi:
2025-07-01 03:05:32.776 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:32.780 else:
2025-07-01 03:05:32.785 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:32.789 elif blo < bhi:
2025-07-01 03:05:32.793 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:32.797
2025-07-01 03:05:32.802 >       yield from g
2025-07-01 03:05:32.806
2025-07-01 03:05:32.810 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:32.815 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:32.819
2025-07-01 03:05:32.823 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:32.828 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:32.832 alo = 458, ahi = 1101
2025-07-01 03:05:32.837 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:32.841 blo = 458, bhi = 1101
2025-07-01 03:05:32.845
2025-07-01 03:05:32.850 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:32.854 r"""
2025-07-01 03:05:32.858 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:32.863 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:32.868 synch point, and intraline difference marking is done on the
2025-07-01 03:05:32.872 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:32.877
2025-07-01 03:05:32.881 Example:
2025-07-01 03:05:32.885
2025-07-01 03:05:32.889 >>> d = Differ()
2025-07-01 03:05:32.893 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:32.898 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:32.902 >>> print(''.join(results), end="")
2025-07-01 03:05:32.906 - abcDefghiJkl
2025-07-01 03:05:32.915 + abcdefGhijkl
2025-07-01 03:05:32.925 """
2025-07-01 03:05:32.929
2025-07-01 03:05:32.933 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:32.937 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:32.942 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:32.946 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:32.950 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:32.954
2025-07-01 03:05:32.959 # search for the pair that matches best without being identical
2025-07-01 03:05:32.963 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:32.967 # on junk -- unless we have to)
2025-07-01 03:05:32.972 for j in range(blo, bhi):
2025-07-01 03:05:32.976 bj = b[j]
2025-07-01 03:05:32.980 cruncher.set_seq2(bj)
2025-07-01 03:05:32.984 for i in range(alo, ahi):
2025-07-01 03:05:32.989 ai = a[i]
2025-07-01 03:05:32.993 if ai == bj:
2025-07-01 03:05:32.997 if eqi is None:
2025-07-01 03:05:33.002 eqi, eqj = i, j
2025-07-01 03:05:33.006 continue
2025-07-01 03:05:33.010 cruncher.set_seq1(ai)
2025-07-01 03:05:33.015 # computing similarity is expensive, so use the quick
2025-07-01 03:05:33.020 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:33.025 # compares by a factor of 3.
2025-07-01 03:05:33.030 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:33.034 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:33.039 # of the computation is cached by cruncher
2025-07-01 03:05:33.043 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:33.048 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:33.052 cruncher.ratio() > best_ratio:
2025-07-01 03:05:33.056 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:33.061 if best_ratio < cutoff:
2025-07-01 03:05:33.065 # no non-identical "pretty close" pair
2025-07-01 03:05:33.070 if eqi is None:
2025-07-01 03:05:33.074 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:33.079 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:33.083 return
2025-07-01 03:05:33.087 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:33.092 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:33.096 else:
2025-07-01 03:05:33.101 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:33.105 eqi = None
2025-07-01 03:05:33.109
2025-07-01 03:05:33.114 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:33.118 # identical
2025-07-01 03:05:33.122
2025-07-01 03:05:33.127 # pump out diffs from before the synch point
2025-07-01 03:05:33.131 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:33.136
2025-07-01 03:05:33.141 # do intraline marking on the synch pair
2025-07-01 03:05:33.145 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:33.150 if eqi is None:
2025-07-01 03:05:33.154 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:33.159 atags = btags = ""
2025-07-01 03:05:33.163 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:33.168 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:33.172 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:33.176 if tag == 'replace':
2025-07-01 03:05:33.181 atags += '^' * la
2025-07-01 03:05:33.185 btags += '^' * lb
2025-07-01 03:05:33.190 elif tag == 'delete':
2025-07-01 03:05:33.194 atags += '-' * la
2025-07-01 03:05:33.198 elif tag == 'insert':
2025-07-01 03:05:33.203 btags += '+' * lb
2025-07-01 03:05:33.207 elif tag == 'equal':
2025-07-01 03:05:33.212 atags += ' ' * la
2025-07-01 03:05:33.216 btags += ' ' * lb
2025-07-01 03:05:33.221 else:
2025-07-01 03:05:33.225 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:33.230 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:33.235 else:
2025-07-01 03:05:33.239 # the synch pair is identical
2025-07-01 03:05:33.243 yield '  ' + aelt
2025-07-01 03:05:33.248
2025-07-01 03:05:33.252 # pump out diffs from after the synch point
2025-07-01 03:05:33.257 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:33.261
2025-07-01 03:05:33.265 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:33.269 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:33.274
2025-07-01 03:05:33.278 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:33.283 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:33.287 alo = 459, ahi = 1101
2025-07-01 03:05:33.292 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:33.296 blo = 459, bhi = 1101
2025-07-01 03:05:33.301
2025-07-01 03:05:33.305 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:33.310 g = []
2025-07-01 03:05:33.315 if alo < ahi:
2025-07-01 03:05:33.319 if blo < bhi:
2025-07-01 03:05:33.324 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:33.329 else:
2025-07-01 03:05:33.334 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:33.338 elif blo < bhi:
2025-07-01 03:05:33.343 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:33.347
2025-07-01 03:05:33.352 >       yield from g
2025-07-01 03:05:33.356
2025-07-01 03:05:33.361 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:33.366 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:33.370
2025-07-01 03:05:33.376 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:33.381 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:33.385 alo = 459, ahi = 1101
2025-07-01 03:05:33.390 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:33.395 blo = 459, bhi = 1101
2025-07-01 03:05:33.399
2025-07-01 03:05:33.404 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:33.408 r"""
2025-07-01 03:05:33.413 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:33.418 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:33.423 synch point, and intraline difference marking is done on the
2025-07-01 03:05:33.428 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:33.432
2025-07-01 03:05:33.437 Example:
2025-07-01 03:05:33.441
2025-07-01 03:05:33.446 >>> d = Differ()
2025-07-01 03:05:33.451 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:33.456 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:33.460 >>> print(''.join(results), end="")
2025-07-01 03:05:33.465 - abcDefghiJkl
2025-07-01 03:05:33.474 + abcdefGhijkl
2025-07-01 03:05:33.483 """
2025-07-01 03:05:33.487
2025-07-01 03:05:33.491 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:33.496 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:33.500 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:33.504 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:33.509 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:33.513
2025-07-01 03:05:33.518 # search for the pair that matches best without being identical
2025-07-01 03:05:33.522 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:33.527 # on junk -- unless we have to)
2025-07-01 03:05:33.531 for j in range(blo, bhi):
2025-07-01 03:05:33.536 bj = b[j]
2025-07-01 03:05:33.540 cruncher.set_seq2(bj)
2025-07-01 03:05:33.544 for i in range(alo, ahi):
2025-07-01 03:05:33.548 ai = a[i]
2025-07-01 03:05:33.553 if ai == bj:
2025-07-01 03:05:33.557 if eqi is None:
2025-07-01 03:05:33.561 eqi, eqj = i, j
2025-07-01 03:05:33.566 continue
2025-07-01 03:05:33.570 cruncher.set_seq1(ai)
2025-07-01 03:05:33.574 # computing similarity is expensive, so use the quick
2025-07-01 03:05:33.579 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:33.583 # compares by a factor of 3.
2025-07-01 03:05:33.588 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:33.593 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:33.597 # of the computation is cached by cruncher
2025-07-01 03:05:33.602 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:33.607 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:33.611 cruncher.ratio() > best_ratio:
2025-07-01 03:05:33.616 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:33.620 if best_ratio < cutoff:
2025-07-01 03:05:33.625 # no non-identical "pretty close" pair
2025-07-01 03:05:33.629 if eqi is None:
2025-07-01 03:05:33.634 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:33.638 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:33.643 return
2025-07-01 03:05:33.647 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:33.651 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:33.656 else:
2025-07-01 03:05:33.660 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:33.664 eqi = None
2025-07-01 03:05:33.669
2025-07-01 03:05:33.673 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:33.677 # identical
2025-07-01 03:05:33.682
2025-07-01 03:05:33.686 # pump out diffs from before the synch point
2025-07-01 03:05:33.690 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:33.695
2025-07-01 03:05:33.699 # do intraline marking on the synch pair
2025-07-01 03:05:33.703 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:33.708 if eqi is None:
2025-07-01 03:05:33.713 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:33.717 atags = btags = ""
2025-07-01 03:05:33.722 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:33.726 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:33.731 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:33.735 if tag == 'replace':
2025-07-01 03:05:33.739 atags += '^' * la
2025-07-01 03:05:33.744 btags += '^' * lb
2025-07-01 03:05:33.748 elif tag == 'delete':
2025-07-01 03:05:33.752 atags += '-' * la
2025-07-01 03:05:33.757 elif tag == 'insert':
2025-07-01 03:05:33.761 btags += '+' * lb
2025-07-01 03:05:33.765 elif tag == 'equal':
2025-07-01 03:05:33.770 atags += ' ' * la
2025-07-01 03:05:33.774 btags += ' ' * lb
2025-07-01 03:05:33.778 else:
2025-07-01 03:05:33.783 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:33.787 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:33.791 else:
2025-07-01 03:05:33.796 # the synch pair is identical
2025-07-01 03:05:33.800 yield '  ' + aelt
2025-07-01 03:05:33.804
2025-07-01 03:05:33.809 # pump out diffs from after the synch point
2025-07-01 03:05:33.814 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:33.818
2025-07-01 03:05:33.822 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:33.827 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:33.832
2025-07-01 03:05:33.837 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:33.841 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:33.845 alo = 460, ahi = 1101
2025-07-01 03:05:33.851 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:33.855 blo = 460, bhi = 1101
2025-07-01 03:05:33.860
2025-07-01 03:05:33.866 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:33.872 g = []
2025-07-01 03:05:33.878 if alo < ahi:
2025-07-01 03:05:33.884 if blo < bhi:
2025-07-01 03:05:33.890 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:33.895 else:
2025-07-01 03:05:33.901 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:33.906 elif blo < bhi:
2025-07-01 03:05:33.912 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:33.918
2025-07-01 03:05:33.924 >       yield from g
2025-07-01 03:05:33.936
2025-07-01 03:05:33.947 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:33.954 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:33.959
2025-07-01 03:05:33.964 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:33.969 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:33.974 alo = 460, ahi = 1101
2025-07-01 03:05:33.979 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:33.984 blo = 460, bhi = 1101
2025-07-01 03:05:33.988
2025-07-01 03:05:33.992 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:33.997 r"""
2025-07-01 03:05:34.002 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:34.006 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:34.011 synch point, and intraline difference marking is done on the
2025-07-01 03:05:34.015 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:34.020
2025-07-01 03:05:34.024 Example:
2025-07-01 03:05:34.029
2025-07-01 03:05:34.033 >>> d = Differ()
2025-07-01 03:05:34.037 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:34.042 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:34.046 >>> print(''.join(results), end="")
2025-07-01 03:05:34.051 - abcDefghiJkl
2025-07-01 03:05:34.063 + abcdefGhijkl
2025-07-01 03:05:34.075 """
2025-07-01 03:05:34.082
2025-07-01 03:05:34.089 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:34.097 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:34.104 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:34.109 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:34.113 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:34.118
2025-07-01 03:05:34.122 # search for the pair that matches best without being identical
2025-07-01 03:05:34.127 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:34.131 # on junk -- unless we have to)
2025-07-01 03:05:34.136 for j in range(blo, bhi):
2025-07-01 03:05:34.140 bj = b[j]
2025-07-01 03:05:34.145 cruncher.set_seq2(bj)
2025-07-01 03:05:34.149 for i in range(alo, ahi):
2025-07-01 03:05:34.154 ai = a[i]
2025-07-01 03:05:34.159 if ai == bj:
2025-07-01 03:05:34.164 if eqi is None:
2025-07-01 03:05:34.168 eqi, eqj = i, j
2025-07-01 03:05:34.173 continue
2025-07-01 03:05:34.177 cruncher.set_seq1(ai)
2025-07-01 03:05:34.182 # computing similarity is expensive, so use the quick
2025-07-01 03:05:34.186 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:34.191 # compares by a factor of 3.
2025-07-01 03:05:34.195 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:34.200 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:34.206 # of the computation is cached by cruncher
2025-07-01 03:05:34.210 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:34.214 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:34.219 cruncher.ratio() > best_ratio:
2025-07-01 03:05:34.223 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:34.228 if best_ratio < cutoff:
2025-07-01 03:05:34.232 # no non-identical "pretty close" pair
2025-07-01 03:05:34.236 if eqi is None:
2025-07-01 03:05:34.241 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:34.245 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:34.249 return
2025-07-01 03:05:34.254 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:34.258 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:34.262 else:
2025-07-01 03:05:34.267 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:34.271 eqi = None
2025-07-01 03:05:34.275
2025-07-01 03:05:34.280 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:34.284 # identical
2025-07-01 03:05:34.288
2025-07-01 03:05:34.293 # pump out diffs from before the synch point
2025-07-01 03:05:34.297 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:34.301
2025-07-01 03:05:34.305 # do intraline marking on the synch pair
2025-07-01 03:05:34.310 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:34.314 if eqi is None:
2025-07-01 03:05:34.318 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:34.323 atags = btags = ""
2025-07-01 03:05:34.327 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:34.331 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:34.336 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:34.340 if tag == 'replace':
2025-07-01 03:05:34.345 atags += '^' * la
2025-07-01 03:05:34.349 btags += '^' * lb
2025-07-01 03:05:34.353 elif tag == 'delete':
2025-07-01 03:05:34.358 atags += '-' * la
2025-07-01 03:05:34.362 elif tag == 'insert':
2025-07-01 03:05:34.366 btags += '+' * lb
2025-07-01 03:05:34.371 elif tag == 'equal':
2025-07-01 03:05:34.375 atags += ' ' * la
2025-07-01 03:05:34.380 btags += ' ' * lb
2025-07-01 03:05:34.384 else:
2025-07-01 03:05:34.389 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:34.393 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:34.397 else:
2025-07-01 03:05:34.402 # the synch pair is identical
2025-07-01 03:05:34.406 yield '  ' + aelt
2025-07-01 03:05:34.410
2025-07-01 03:05:34.415 # pump out diffs from after the synch point
2025-07-01 03:05:34.419 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:34.423
2025-07-01 03:05:34.428 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:34.432 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:34.437
2025-07-01 03:05:34.441 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:34.446 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:34.450 alo = 461, ahi = 1101
2025-07-01 03:05:34.455 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:34.460 blo = 461, bhi = 1101
2025-07-01 03:05:34.464
2025-07-01 03:05:34.468 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:34.473 g = []
2025-07-01 03:05:34.477 if alo < ahi:
2025-07-01 03:05:34.482 if blo < bhi:
2025-07-01 03:05:34.486 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:34.491 else:
2025-07-01 03:05:34.496 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:34.500 elif blo < bhi:
2025-07-01 03:05:34.505 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:34.509
2025-07-01 03:05:34.513 >       yield from g
2025-07-01 03:05:34.517
2025-07-01 03:05:34.521 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:34.526 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:34.530
2025-07-01 03:05:34.535 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:34.539 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:34.544 alo = 461, ahi = 1101
2025-07-01 03:05:34.548 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:34.554 blo = 461, bhi = 1101
2025-07-01 03:05:34.559
2025-07-01 03:05:34.565 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:34.571 r"""
2025-07-01 03:05:34.576 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:34.581 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:34.585 synch point, and intraline difference marking is done on the
2025-07-01 03:05:34.590 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:34.594
2025-07-01 03:05:34.598 Example:
2025-07-01 03:05:34.602
2025-07-01 03:05:34.606 >>> d = Differ()
2025-07-01 03:05:34.611 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:34.616 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:34.620 >>> print(''.join(results), end="")
2025-07-01 03:05:34.625 - abcDefghiJkl
2025-07-01 03:05:34.635 + abcdefGhijkl
2025-07-01 03:05:34.644 """
2025-07-01 03:05:34.648
2025-07-01 03:05:34.653 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:34.657 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:34.661 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:34.666 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:34.670 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:34.675
2025-07-01 03:05:34.679 # search for the pair that matches best without being identical
2025-07-01 03:05:34.684 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:34.688 # on junk -- unless we have to)
2025-07-01 03:05:34.693 for j in range(blo, bhi):
2025-07-01 03:05:34.697 bj = b[j]
2025-07-01 03:05:34.702 cruncher.set_seq2(bj)
2025-07-01 03:05:34.706 for i in range(alo, ahi):
2025-07-01 03:05:34.711 ai = a[i]
2025-07-01 03:05:34.715 if ai == bj:
2025-07-01 03:05:34.720 if eqi is None:
2025-07-01 03:05:34.724 eqi, eqj = i, j
2025-07-01 03:05:34.729 continue
2025-07-01 03:05:34.734 cruncher.set_seq1(ai)
2025-07-01 03:05:34.739 # computing similarity is expensive, so use the quick
2025-07-01 03:05:34.744 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:34.748 # compares by a factor of 3.
2025-07-01 03:05:34.753 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:34.757 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:34.762 # of the computation is cached by cruncher
2025-07-01 03:05:34.766 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:34.771 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:34.775 cruncher.ratio() > best_ratio:
2025-07-01 03:05:34.780 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:34.785 if best_ratio < cutoff:
2025-07-01 03:05:34.790 # no non-identical "pretty close" pair
2025-07-01 03:05:34.794 if eqi is None:
2025-07-01 03:05:34.799 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:34.804 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:34.809 return
2025-07-01 03:05:34.814 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:34.819 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:34.823 else:
2025-07-01 03:05:34.828 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:34.832 eqi = None
2025-07-01 03:05:34.836
2025-07-01 03:05:34.842 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:34.846 # identical
2025-07-01 03:05:34.851
2025-07-01 03:05:34.855 # pump out diffs from before the synch point
2025-07-01 03:05:34.860 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:34.864
2025-07-01 03:05:34.869 # do intraline marking on the synch pair
2025-07-01 03:05:34.874 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:34.878 if eqi is None:
2025-07-01 03:05:34.882 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:34.887 atags = btags = ""
2025-07-01 03:05:34.891 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:34.896 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:34.901 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:34.906 if tag == 'replace':
2025-07-01 03:05:34.910 atags += '^' * la
2025-07-01 03:05:34.915 btags += '^' * lb
2025-07-01 03:05:34.919 elif tag == 'delete':
2025-07-01 03:05:34.924 atags += '-' * la
2025-07-01 03:05:34.928 elif tag == 'insert':
2025-07-01 03:05:34.933 btags += '+' * lb
2025-07-01 03:05:34.937 elif tag == 'equal':
2025-07-01 03:05:34.942 atags += ' ' * la
2025-07-01 03:05:34.946 btags += ' ' * lb
2025-07-01 03:05:34.950 else:
2025-07-01 03:05:34.955 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:34.959 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:34.963 else:
2025-07-01 03:05:34.969 # the synch pair is identical
2025-07-01 03:05:34.974 yield '  ' + aelt
2025-07-01 03:05:34.979
2025-07-01 03:05:34.983 # pump out diffs from after the synch point
2025-07-01 03:05:34.987 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:34.992
2025-07-01 03:05:34.996 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:35.001 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:35.006
2025-07-01 03:05:35.011 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:35.018 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:35.023 alo = 462, ahi = 1101
2025-07-01 03:05:35.031 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:35.035 blo = 462, bhi = 1101
2025-07-01 03:05:35.040
2025-07-01 03:05:35.045 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:35.049 g = []
2025-07-01 03:05:35.054 if alo < ahi:
2025-07-01 03:05:35.058 if blo < bhi:
2025-07-01 03:05:35.063 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:35.068 else:
2025-07-01 03:05:35.072 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:35.077 elif blo < bhi:
2025-07-01 03:05:35.081 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:35.086
2025-07-01 03:05:35.090 >       yield from g
2025-07-01 03:05:35.094
2025-07-01 03:05:35.099 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:35.103 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:35.109
2025-07-01 03:05:35.115 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:35.120 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:35.125 alo = 462, ahi = 1101
2025-07-01 03:05:35.132 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:35.137 blo = 462, bhi = 1101
2025-07-01 03:05:35.142
2025-07-01 03:05:35.146 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:35.150 r"""
2025-07-01 03:05:35.155 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:35.159 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:35.164 synch point, and intraline difference marking is done on the
2025-07-01 03:05:35.168 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:35.173
2025-07-01 03:05:35.178 Example:
2025-07-01 03:05:35.184
2025-07-01 03:05:35.188 >>> d = Differ()
2025-07-01 03:05:35.192 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:35.197 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:35.201 >>> print(''.join(results), end="")
2025-07-01 03:05:35.206 - abcDefghiJkl
2025-07-01 03:05:35.215 + abcdefGhijkl
2025-07-01 03:05:35.223 """
2025-07-01 03:05:35.227
2025-07-01 03:05:35.232 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:35.236 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:35.241 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:35.245 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:35.250 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:35.254
2025-07-01 03:05:35.259 # search for the pair that matches best without being identical
2025-07-01 03:05:35.263 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:35.268 # on junk -- unless we have to)
2025-07-01 03:05:35.272 for j in range(blo, bhi):
2025-07-01 03:05:35.276 bj = b[j]
2025-07-01 03:05:35.281 cruncher.set_seq2(bj)
2025-07-01 03:05:35.285 for i in range(alo, ahi):
2025-07-01 03:05:35.289 ai = a[i]
2025-07-01 03:05:35.294 if ai == bj:
2025-07-01 03:05:35.298 if eqi is None:
2025-07-01 03:05:35.303 eqi, eqj = i, j
2025-07-01 03:05:35.307 continue
2025-07-01 03:05:35.311 cruncher.set_seq1(ai)
2025-07-01 03:05:35.316 # computing similarity is expensive, so use the quick
2025-07-01 03:05:35.320 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:35.325 # compares by a factor of 3.
2025-07-01 03:05:35.329 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:35.334 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:35.339 # of the computation is cached by cruncher
2025-07-01 03:05:35.343 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:35.347 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:35.352 cruncher.ratio() > best_ratio:
2025-07-01 03:05:35.356 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:35.361 if best_ratio < cutoff:
2025-07-01 03:05:35.365 # no non-identical "pretty close" pair
2025-07-01 03:05:35.369 if eqi is None:
2025-07-01 03:05:35.374 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:35.378 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:35.383 return
2025-07-01 03:05:35.387 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:35.393 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:35.401 else:
2025-07-01 03:05:35.411 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:35.418 eqi = None
2025-07-01 03:05:35.423
2025-07-01 03:05:35.428 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:35.433 # identical
2025-07-01 03:05:35.437
2025-07-01 03:05:35.441 # pump out diffs from before the synch point
2025-07-01 03:05:35.446 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:35.450
2025-07-01 03:05:35.455 # do intraline marking on the synch pair
2025-07-01 03:05:35.459 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:35.464 if eqi is None:
2025-07-01 03:05:35.468 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:35.473 atags = btags = ""
2025-07-01 03:05:35.477 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:35.482 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:35.486 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:35.490 if tag == 'replace':
2025-07-01 03:05:35.495 atags += '^' * la
2025-07-01 03:05:35.499 btags += '^' * lb
2025-07-01 03:05:35.503 elif tag == 'delete':
2025-07-01 03:05:35.508 atags += '-' * la
2025-07-01 03:05:35.512 elif tag == 'insert':
2025-07-01 03:05:35.517 btags += '+' * lb
2025-07-01 03:05:35.522 elif tag == 'equal':
2025-07-01 03:05:35.526 atags += ' ' * la
2025-07-01 03:05:35.530 btags += ' ' * lb
2025-07-01 03:05:35.535 else:
2025-07-01 03:05:35.539 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:35.544 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:35.548 else:
2025-07-01 03:05:35.553 # the synch pair is identical
2025-07-01 03:05:35.558 yield '  ' + aelt
2025-07-01 03:05:35.562
2025-07-01 03:05:35.567 # pump out diffs from after the synch point
2025-07-01 03:05:35.572 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:35.576
2025-07-01 03:05:35.581 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:35.586 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:35.590
2025-07-01 03:05:35.595 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:35.600 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:35.605 alo = 463, ahi = 1101
2025-07-01 03:05:35.610 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:35.614 blo = 463, bhi = 1101
2025-07-01 03:05:35.618
2025-07-01 03:05:35.623 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:35.628 g = []
2025-07-01 03:05:35.632 if alo < ahi:
2025-07-01 03:05:35.637 if blo < bhi:
2025-07-01 03:05:35.642 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:35.647 else:
2025-07-01 03:05:35.652 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:35.656 elif blo < bhi:
2025-07-01 03:05:35.661 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:35.666
2025-07-01 03:05:35.670 >       yield from g
2025-07-01 03:05:35.675
2025-07-01 03:05:35.679 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:35.684 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:35.688
2025-07-01 03:05:35.693 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:35.697 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:35.702 alo = 463, ahi = 1101
2025-07-01 03:05:35.706 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:35.711 blo = 463, bhi = 1101
2025-07-01 03:05:35.715
2025-07-01 03:05:35.720 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:35.725 r"""
2025-07-01 03:05:35.730 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:35.734 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:35.739 synch point, and intraline difference marking is done on the
2025-07-01 03:05:35.743 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:35.747
2025-07-01 03:05:35.752 Example:
2025-07-01 03:05:35.756
2025-07-01 03:05:35.760 >>> d = Differ()
2025-07-01 03:05:35.765 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:35.770 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:35.774 >>> print(''.join(results), end="")
2025-07-01 03:05:35.778 - abcDefghiJkl
2025-07-01 03:05:35.787 + abcdefGhijkl
2025-07-01 03:05:35.796 """
2025-07-01 03:05:35.800
2025-07-01 03:05:35.805 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:35.810 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:35.815 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:35.819 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:35.824 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:35.829
2025-07-01 03:05:35.834 # search for the pair that matches best without being identical
2025-07-01 03:05:35.838 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:35.843 # on junk -- unless we have to)
2025-07-01 03:05:35.847 for j in range(blo, bhi):
2025-07-01 03:05:35.852 bj = b[j]
2025-07-01 03:05:35.857 cruncher.set_seq2(bj)
2025-07-01 03:05:35.861 for i in range(alo, ahi):
2025-07-01 03:05:35.865 ai = a[i]
2025-07-01 03:05:35.870 if ai == bj:
2025-07-01 03:05:35.874 if eqi is None:
2025-07-01 03:05:35.878 eqi, eqj = i, j
2025-07-01 03:05:35.883 continue
2025-07-01 03:05:35.887 cruncher.set_seq1(ai)
2025-07-01 03:05:35.891 # computing similarity is expensive, so use the quick
2025-07-01 03:05:35.896 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:35.901 # compares by a factor of 3.
2025-07-01 03:05:35.906 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:35.910 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:35.915 # of the computation is cached by cruncher
2025-07-01 03:05:35.919 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:35.924 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:35.929 cruncher.ratio() > best_ratio:
2025-07-01 03:05:35.934 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:35.938 if best_ratio < cutoff:
2025-07-01 03:05:35.943 # no non-identical "pretty close" pair
2025-07-01 03:05:35.947 if eqi is None:
2025-07-01 03:05:35.952 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:35.956 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:35.961 return
2025-07-01 03:05:35.966 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:35.972 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:35.978 else:
2025-07-01 03:05:35.982 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:35.987 eqi = None
2025-07-01 03:05:35.992
2025-07-01 03:05:35.998 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:36.004 # identical
2025-07-01 03:05:36.008
2025-07-01 03:05:36.013 # pump out diffs from before the synch point
2025-07-01 03:05:36.018 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:36.022
2025-07-01 03:05:36.027 # do intraline marking on the synch pair
2025-07-01 03:05:36.031 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:36.035 if eqi is None:
2025-07-01 03:05:36.041 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:36.045 atags = btags = ""
2025-07-01 03:05:36.050 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:36.055 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:36.060 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:36.065 if tag == 'replace':
2025-07-01 03:05:36.069 atags += '^' * la
2025-07-01 03:05:36.074 btags += '^' * lb
2025-07-01 03:05:36.078 elif tag == 'delete':
2025-07-01 03:05:36.082 atags += '-' * la
2025-07-01 03:05:36.086 elif tag == 'insert':
2025-07-01 03:05:36.091 btags += '+' * lb
2025-07-01 03:05:36.095 elif tag == 'equal':
2025-07-01 03:05:36.100 atags += ' ' * la
2025-07-01 03:05:36.104 btags += ' ' * lb
2025-07-01 03:05:36.108 else:
2025-07-01 03:05:36.113 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:36.117 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:36.122 else:
2025-07-01 03:05:36.126 # the synch pair is identical
2025-07-01 03:05:36.130 yield '  ' + aelt
2025-07-01 03:05:36.135
2025-07-01 03:05:36.140 # pump out diffs from after the synch point
2025-07-01 03:05:36.145 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:36.149
2025-07-01 03:05:36.155 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:36.159 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:36.163
2025-07-01 03:05:36.168 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:36.173 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:36.177 alo = 466, ahi = 1101
2025-07-01 03:05:36.182 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:36.189 blo = 466, bhi = 1101
2025-07-01 03:05:36.194
2025-07-01 03:05:36.199 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:36.203 g = []
2025-07-01 03:05:36.208 if alo < ahi:
2025-07-01 03:05:36.212 if blo < bhi:
2025-07-01 03:05:36.217 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:36.222 else:
2025-07-01 03:05:36.227 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:36.233 elif blo < bhi:
2025-07-01 03:05:36.239 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:36.245
2025-07-01 03:05:36.250 >       yield from g
2025-07-01 03:05:36.254
2025-07-01 03:05:36.259 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:36.263 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:36.268
2025-07-01 03:05:36.273 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:36.279 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:36.285 alo = 466, ahi = 1101
2025-07-01 03:05:36.292 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:36.298 blo = 466, bhi = 1101
2025-07-01 03:05:36.303
2025-07-01 03:05:36.307 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:36.312 r"""
2025-07-01 03:05:36.316 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:36.321 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:36.325 synch point, and intraline difference marking is done on the
2025-07-01 03:05:36.330 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:36.335
2025-07-01 03:05:36.339 Example:
2025-07-01 03:05:36.344
2025-07-01 03:05:36.349 >>> d = Differ()
2025-07-01 03:05:36.354 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:36.359 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:36.364 >>> print(''.join(results), end="")
2025-07-01 03:05:36.368 - abcDefghiJkl
2025-07-01 03:05:36.378 + abcdefGhijkl
2025-07-01 03:05:36.390 """
2025-07-01 03:05:36.395
2025-07-01 03:05:36.400 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:36.405 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:36.409 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:36.414 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:36.418 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:36.423
2025-07-01 03:05:36.428 # search for the pair that matches best without being identical
2025-07-01 03:05:36.433 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:36.438 # on junk -- unless we have to)
2025-07-01 03:05:36.442 for j in range(blo, bhi):
2025-07-01 03:05:36.447 bj = b[j]
2025-07-01 03:05:36.453 cruncher.set_seq2(bj)
2025-07-01 03:05:36.458 for i in range(alo, ahi):
2025-07-01 03:05:36.462 ai = a[i]
2025-07-01 03:05:36.467 if ai == bj:
2025-07-01 03:05:36.473 if eqi is None:
2025-07-01 03:05:36.477 eqi, eqj = i, j
2025-07-01 03:05:36.482 continue
2025-07-01 03:05:36.487 cruncher.set_seq1(ai)
2025-07-01 03:05:36.491 # computing similarity is expensive, so use the quick
2025-07-01 03:05:36.495 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:36.500 # compares by a factor of 3.
2025-07-01 03:05:36.505 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:36.509 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:36.513 # of the computation is cached by cruncher
2025-07-01 03:05:36.518 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:36.524 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:36.528 cruncher.ratio() > best_ratio:
2025-07-01 03:05:36.533 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:36.537 if best_ratio < cutoff:
2025-07-01 03:05:36.542 # no non-identical "pretty close" pair
2025-07-01 03:05:36.547 if eqi is None:
2025-07-01 03:05:36.552 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:36.557 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:36.562 return
2025-07-01 03:05:36.567 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:36.572 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:36.577 else:
2025-07-01 03:05:36.582 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:36.587 eqi = None
2025-07-01 03:05:36.592
2025-07-01 03:05:36.597 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:36.602 # identical
2025-07-01 03:05:36.607
2025-07-01 03:05:36.613 # pump out diffs from before the synch point
2025-07-01 03:05:36.619 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:36.623
2025-07-01 03:05:36.628 # do intraline marking on the synch pair
2025-07-01 03:05:36.633 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:36.639 if eqi is None:
2025-07-01 03:05:36.644 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:36.650 atags = btags = ""
2025-07-01 03:05:36.656 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:36.664 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:36.669 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:36.673 if tag == 'replace':
2025-07-01 03:05:36.678 atags += '^' * la
2025-07-01 03:05:36.682 btags += '^' * lb
2025-07-01 03:05:36.687 elif tag == 'delete':
2025-07-01 03:05:36.691 atags += '-' * la
2025-07-01 03:05:36.696 elif tag == 'insert':
2025-07-01 03:05:36.700 btags += '+' * lb
2025-07-01 03:05:36.705 elif tag == 'equal':
2025-07-01 03:05:36.709 atags += ' ' * la
2025-07-01 03:05:36.713 btags += ' ' * lb
2025-07-01 03:05:36.718 else:
2025-07-01 03:05:36.722 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:36.727 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:36.732 else:
2025-07-01 03:05:36.738 # the synch pair is identical
2025-07-01 03:05:36.742 yield '  ' + aelt
2025-07-01 03:05:36.747
2025-07-01 03:05:36.751 # pump out diffs from after the synch point
2025-07-01 03:05:36.755 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:36.760
2025-07-01 03:05:36.764 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:36.769 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:36.773
2025-07-01 03:05:36.778 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:36.782 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:36.787 alo = 467, ahi = 1101
2025-07-01 03:05:36.791 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:36.796 blo = 467, bhi = 1101
2025-07-01 03:05:36.800
2025-07-01 03:05:36.804 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:36.809 g = []
2025-07-01 03:05:36.813 if alo < ahi:
2025-07-01 03:05:36.817 if blo < bhi:
2025-07-01 03:05:36.822 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:36.826 else:
2025-07-01 03:05:36.831 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:36.835 elif blo < bhi:
2025-07-01 03:05:36.839 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:36.843
2025-07-01 03:05:36.848 >       yield from g
2025-07-01 03:05:36.852
2025-07-01 03:05:36.857 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:36.861 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:36.865
2025-07-01 03:05:36.870 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:36.874 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:36.879 alo = 467, ahi = 1101
2025-07-01 03:05:36.883 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:36.887 blo = 467, bhi = 1101
2025-07-01 03:05:36.892
2025-07-01 03:05:36.896 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:36.901 r"""
2025-07-01 03:05:36.905 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:36.910 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:36.914 synch point, and intraline difference marking is done on the
2025-07-01 03:05:36.919 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:36.923
2025-07-01 03:05:36.927 Example:
2025-07-01 03:05:36.931
2025-07-01 03:05:36.935 >>> d = Differ()
2025-07-01 03:05:36.940 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:36.944 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:36.949 >>> print(''.join(results), end="")
2025-07-01 03:05:36.953 - abcDefghiJkl
2025-07-01 03:05:36.961 + abcdefGhijkl
2025-07-01 03:05:36.970 """
2025-07-01 03:05:36.975
2025-07-01 03:05:36.980 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:36.984 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:36.989 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:36.993 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:36.998 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:37.002
2025-07-01 03:05:37.007 # search for the pair that matches best without being identical
2025-07-01 03:05:37.014 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:37.020 # on junk -- unless we have to)
2025-07-01 03:05:37.027 for j in range(blo, bhi):
2025-07-01 03:05:37.034 bj = b[j]
2025-07-01 03:05:37.040 cruncher.set_seq2(bj)
2025-07-01 03:05:37.047 for i in range(alo, ahi):
2025-07-01 03:05:37.052 ai = a[i]
2025-07-01 03:05:37.057 if ai == bj:
2025-07-01 03:05:37.062 if eqi is None:
2025-07-01 03:05:37.067 eqi, eqj = i, j
2025-07-01 03:05:37.071 continue
2025-07-01 03:05:37.076 cruncher.set_seq1(ai)
2025-07-01 03:05:37.081 # computing similarity is expensive, so use the quick
2025-07-01 03:05:37.085 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:37.090 # compares by a factor of 3.
2025-07-01 03:05:37.095 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:37.100 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:37.104 # of the computation is cached by cruncher
2025-07-01 03:05:37.109 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:37.114 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:37.118 cruncher.ratio() > best_ratio:
2025-07-01 03:05:37.124 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:37.128 if best_ratio < cutoff:
2025-07-01 03:05:37.133 # no non-identical "pretty close" pair
2025-07-01 03:05:37.138 if eqi is None:
2025-07-01 03:05:37.143 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:37.147 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:37.152 return
2025-07-01 03:05:37.157 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:37.162 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:37.166 else:
2025-07-01 03:05:37.171 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:37.175 eqi = None
2025-07-01 03:05:37.180
2025-07-01 03:05:37.184 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:37.189 # identical
2025-07-01 03:05:37.193
2025-07-01 03:05:37.198 # pump out diffs from before the synch point
2025-07-01 03:05:37.202 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:37.207
2025-07-01 03:05:37.211 # do intraline marking on the synch pair
2025-07-01 03:05:37.216 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:37.220 if eqi is None:
2025-07-01 03:05:37.224 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:37.229 atags = btags = ""
2025-07-01 03:05:37.233 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:37.238 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:37.242 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:37.246 if tag == 'replace':
2025-07-01 03:05:37.251 atags += '^' * la
2025-07-01 03:05:37.256 btags += '^' * lb
2025-07-01 03:05:37.262 elif tag == 'delete':
2025-07-01 03:05:37.268 atags += '-' * la
2025-07-01 03:05:37.274 elif tag == 'insert':
2025-07-01 03:05:37.280 btags += '+' * lb
2025-07-01 03:05:37.285 elif tag == 'equal':
2025-07-01 03:05:37.291 atags += ' ' * la
2025-07-01 03:05:37.297 btags += ' ' * lb
2025-07-01 03:05:37.303 else:
2025-07-01 03:05:37.308 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:37.314 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:37.320 else:
2025-07-01 03:05:37.326 # the synch pair is identical
2025-07-01 03:05:37.331 yield '  ' + aelt
2025-07-01 03:05:37.337
2025-07-01 03:05:37.342 # pump out diffs from after the synch point
2025-07-01 03:05:37.346 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:37.351
2025-07-01 03:05:37.355 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:37.359 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:37.364
2025-07-01 03:05:37.368 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:37.373 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:37.377 alo = 468, ahi = 1101
2025-07-01 03:05:37.382 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:37.386 blo = 468, bhi = 1101
2025-07-01 03:05:37.390
2025-07-01 03:05:37.395 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:37.399 g = []
2025-07-01 03:05:37.403 if alo < ahi:
2025-07-01 03:05:37.407 if blo < bhi:
2025-07-01 03:05:37.412 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:37.416 else:
2025-07-01 03:05:37.421 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:37.425 elif blo < bhi:
2025-07-01 03:05:37.429 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:37.434
2025-07-01 03:05:37.438 >       yield from g
2025-07-01 03:05:37.442
2025-07-01 03:05:37.446 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:37.451 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:37.456
2025-07-01 03:05:37.461 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:37.465 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:37.470 alo = 468, ahi = 1101
2025-07-01 03:05:37.475 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:37.479 blo = 468, bhi = 1101
2025-07-01 03:05:37.483
2025-07-01 03:05:37.487 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:37.492 r"""
2025-07-01 03:05:37.496 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:37.500 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:37.505 synch point, and intraline difference marking is done on the
2025-07-01 03:05:37.509 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:37.513
2025-07-01 03:05:37.518 Example:
2025-07-01 03:05:37.522
2025-07-01 03:05:37.526 >>> d = Differ()
2025-07-01 03:05:37.531 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:37.535 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:37.539 >>> print(''.join(results), end="")
2025-07-01 03:05:37.544 - abcDefghiJkl
2025-07-01 03:05:37.553 + abcdefGhijkl
2025-07-01 03:05:37.562 """
2025-07-01 03:05:37.567
2025-07-01 03:05:37.571 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:37.576 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:37.580 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:37.585 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:37.589 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:37.593
2025-07-01 03:05:37.598 # search for the pair that matches best without being identical
2025-07-01 03:05:37.602 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:37.607 # on junk -- unless we have to)
2025-07-01 03:05:37.611 for j in range(blo, bhi):
2025-07-01 03:05:37.615 bj = b[j]
2025-07-01 03:05:37.620 cruncher.set_seq2(bj)
2025-07-01 03:05:37.624 for i in range(alo, ahi):
2025-07-01 03:05:37.629 ai = a[i]
2025-07-01 03:05:37.633 if ai == bj:
2025-07-01 03:05:37.637 if eqi is None:
2025-07-01 03:05:37.642 eqi, eqj = i, j
2025-07-01 03:05:37.646 continue
2025-07-01 03:05:37.650 cruncher.set_seq1(ai)
2025-07-01 03:05:37.655 # computing similarity is expensive, so use the quick
2025-07-01 03:05:37.660 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:37.664 # compares by a factor of 3.
2025-07-01 03:05:37.669 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:37.674 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:37.678 # of the computation is cached by cruncher
2025-07-01 03:05:37.683 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:37.687 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:37.691 cruncher.ratio() > best_ratio:
2025-07-01 03:05:37.696 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:37.700 if best_ratio < cutoff:
2025-07-01 03:05:37.705 # no non-identical "pretty close" pair
2025-07-01 03:05:37.709 if eqi is None:
2025-07-01 03:05:37.714 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:37.718 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:37.723 return
2025-07-01 03:05:37.727 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:37.731 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:37.735 else:
2025-07-01 03:05:37.740 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:37.744 eqi = None
2025-07-01 03:05:37.748
2025-07-01 03:05:37.753 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:37.757 # identical
2025-07-01 03:05:37.761
2025-07-01 03:05:37.766 # pump out diffs from before the synch point
2025-07-01 03:05:37.770 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:37.775
2025-07-01 03:05:37.779 # do intraline marking on the synch pair
2025-07-01 03:05:37.783 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:37.788 if eqi is None:
2025-07-01 03:05:37.792 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:37.796 atags = btags = ""
2025-07-01 03:05:37.801 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:37.805 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:37.809 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:37.814 if tag == 'replace':
2025-07-01 03:05:37.818 atags += '^' * la
2025-07-01 03:05:37.822 btags += '^' * lb
2025-07-01 03:05:37.827 elif tag == 'delete':
2025-07-01 03:05:37.831 atags += '-' * la
2025-07-01 03:05:37.835 elif tag == 'insert':
2025-07-01 03:05:37.840 btags += '+' * lb
2025-07-01 03:05:37.844 elif tag == 'equal':
2025-07-01 03:05:37.849 atags += ' ' * la
2025-07-01 03:05:37.853 btags += ' ' * lb
2025-07-01 03:05:37.857 else:
2025-07-01 03:05:37.862 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:37.866 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:37.870 else:
2025-07-01 03:05:37.875 # the synch pair is identical
2025-07-01 03:05:37.879 yield '  ' + aelt
2025-07-01 03:05:37.884
2025-07-01 03:05:37.888 # pump out diffs from after the synch point
2025-07-01 03:05:37.893 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:37.897
2025-07-01 03:05:37.901 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:37.906 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:37.910
2025-07-01 03:05:37.914 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:37.919 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:37.923 alo = 469, ahi = 1101
2025-07-01 03:05:37.928 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:37.932 blo = 469, bhi = 1101
2025-07-01 03:05:37.937
2025-07-01 03:05:37.941 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:37.945 g = []
2025-07-01 03:05:37.950 if alo < ahi:
2025-07-01 03:05:37.954 if blo < bhi:
2025-07-01 03:05:37.959 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:37.963 else:
2025-07-01 03:05:37.967 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:37.972 elif blo < bhi:
2025-07-01 03:05:37.976 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:37.981
2025-07-01 03:05:37.985 >       yield from g
2025-07-01 03:05:37.989
2025-07-01 03:05:37.993 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:37.997 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:38.002
2025-07-01 03:05:38.008 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:38.014 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:38.020 alo = 469, ahi = 1101
2025-07-01 03:05:38.026 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:38.032 blo = 469, bhi = 1101
2025-07-01 03:05:38.038
2025-07-01 03:05:38.044 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:38.050 r"""
2025-07-01 03:05:38.056 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:38.063 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:38.068 synch point, and intraline difference marking is done on the
2025-07-01 03:05:38.075 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:38.080
2025-07-01 03:05:38.086 Example:
2025-07-01 03:05:38.092
2025-07-01 03:05:38.098 >>> d = Differ()
2025-07-01 03:05:38.104 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:38.110 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:38.116 >>> print(''.join(results), end="")
2025-07-01 03:05:38.122 - abcDefghiJkl
2025-07-01 03:05:38.133 + abcdefGhijkl
2025-07-01 03:05:38.145 """
2025-07-01 03:05:38.150
2025-07-01 03:05:38.155 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:38.159 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:38.164 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:38.168 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:38.173 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:38.177
2025-07-01 03:05:38.181 # search for the pair that matches best without being identical
2025-07-01 03:05:38.186 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:38.190 # on junk -- unless we have to)
2025-07-01 03:05:38.194 for j in range(blo, bhi):
2025-07-01 03:05:38.199 bj = b[j]
2025-07-01 03:05:38.203 cruncher.set_seq2(bj)
2025-07-01 03:05:38.207 for i in range(alo, ahi):
2025-07-01 03:05:38.211 ai = a[i]
2025-07-01 03:05:38.215 if ai == bj:
2025-07-01 03:05:38.220 if eqi is None:
2025-07-01 03:05:38.224 eqi, eqj = i, j
2025-07-01 03:05:38.228 continue
2025-07-01 03:05:38.232 cruncher.set_seq1(ai)
2025-07-01 03:05:38.236 # computing similarity is expensive, so use the quick
2025-07-01 03:05:38.241 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:38.245 # compares by a factor of 3.
2025-07-01 03:05:38.249 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:38.254 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:38.258 # of the computation is cached by cruncher
2025-07-01 03:05:38.262 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:38.267 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:38.271 cruncher.ratio() > best_ratio:
2025-07-01 03:05:38.276 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:38.281 if best_ratio < cutoff:
2025-07-01 03:05:38.285 # no non-identical "pretty close" pair
2025-07-01 03:05:38.289 if eqi is None:
2025-07-01 03:05:38.294 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:38.298 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:38.302 return
2025-07-01 03:05:38.307 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:38.311 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:38.315 else:
2025-07-01 03:05:38.319 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:38.324 eqi = None
2025-07-01 03:05:38.328
2025-07-01 03:05:38.332 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:38.336 # identical
2025-07-01 03:05:38.341
2025-07-01 03:05:38.345 # pump out diffs from before the synch point
2025-07-01 03:05:38.349 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:38.353
2025-07-01 03:05:38.358 # do intraline marking on the synch pair
2025-07-01 03:05:38.362 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:38.366 if eqi is None:
2025-07-01 03:05:38.371 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:38.375 atags = btags = ""
2025-07-01 03:05:38.379 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:38.384 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:38.388 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:38.392 if tag == 'replace':
2025-07-01 03:05:38.397 atags += '^' * la
2025-07-01 03:05:38.401 btags += '^' * lb
2025-07-01 03:05:38.406 elif tag == 'delete':
2025-07-01 03:05:38.410 atags += '-' * la
2025-07-01 03:05:38.414 elif tag == 'insert':
2025-07-01 03:05:38.419 btags += '+' * lb
2025-07-01 03:05:38.423 elif tag == 'equal':
2025-07-01 03:05:38.427 atags += ' ' * la
2025-07-01 03:05:38.432 btags += ' ' * lb
2025-07-01 03:05:38.436 else:
2025-07-01 03:05:38.441 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:38.445 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:38.449 else:
2025-07-01 03:05:38.454 # the synch pair is identical
2025-07-01 03:05:38.458 yield '  ' + aelt
2025-07-01 03:05:38.462
2025-07-01 03:05:38.467 # pump out diffs from after the synch point
2025-07-01 03:05:38.471 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:38.475
2025-07-01 03:05:38.480 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:38.484 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:38.489
2025-07-01 03:05:38.493 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:38.498 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:38.502 alo = 470, ahi = 1101
2025-07-01 03:05:38.507 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:38.511 blo = 470, bhi = 1101
2025-07-01 03:05:38.515
2025-07-01 03:05:38.520 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:38.524 g = []
2025-07-01 03:05:38.528 if alo < ahi:
2025-07-01 03:05:38.532 if blo < bhi:
2025-07-01 03:05:38.537 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:38.541 else:
2025-07-01 03:05:38.545 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:38.550 elif blo < bhi:
2025-07-01 03:05:38.554 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:38.559
2025-07-01 03:05:38.564 >       yield from g
2025-07-01 03:05:38.568
2025-07-01 03:05:38.573 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:38.578 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:38.582
2025-07-01 03:05:38.587 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:38.591 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:38.596 alo = 470, ahi = 1101
2025-07-01 03:05:38.601 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:38.606 blo = 470, bhi = 1101
2025-07-01 03:05:38.610
2025-07-01 03:05:38.615 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:38.619 r"""
2025-07-01 03:05:38.624 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:38.628 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:38.633 synch point, and intraline difference marking is done on the
2025-07-01 03:05:38.637 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:38.641
2025-07-01 03:05:38.646 Example:
2025-07-01 03:05:38.650
2025-07-01 03:05:38.655 >>> d = Differ()
2025-07-01 03:05:38.660 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:38.665 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:38.669 >>> print(''.join(results), end="")
2025-07-01 03:05:38.674 - abcDefghiJkl
2025-07-01 03:05:38.682 + abcdefGhijkl
2025-07-01 03:05:38.691 """
2025-07-01 03:05:38.695
2025-07-01 03:05:38.699 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:38.704 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:38.708 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:38.712 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:38.717 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:38.721
2025-07-01 03:05:38.725 # search for the pair that matches best without being identical
2025-07-01 03:05:38.729 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:38.734 # on junk -- unless we have to)
2025-07-01 03:05:38.738 for j in range(blo, bhi):
2025-07-01 03:05:38.742 bj = b[j]
2025-07-01 03:05:38.746 cruncher.set_seq2(bj)
2025-07-01 03:05:38.751 for i in range(alo, ahi):
2025-07-01 03:05:38.755 ai = a[i]
2025-07-01 03:05:38.759 if ai == bj:
2025-07-01 03:05:38.763 if eqi is None:
2025-07-01 03:05:38.768 eqi, eqj = i, j
2025-07-01 03:05:38.772 continue
2025-07-01 03:05:38.776 cruncher.set_seq1(ai)
2025-07-01 03:05:38.781 # computing similarity is expensive, so use the quick
2025-07-01 03:05:38.785 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:38.789 # compares by a factor of 3.
2025-07-01 03:05:38.794 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:38.798 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:38.803 # of the computation is cached by cruncher
2025-07-01 03:05:38.807 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:38.811 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:38.816 cruncher.ratio() > best_ratio:
2025-07-01 03:05:38.820 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:38.824 if best_ratio < cutoff:
2025-07-01 03:05:38.829 # no non-identical "pretty close" pair
2025-07-01 03:05:38.833 if eqi is None:
2025-07-01 03:05:38.837 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:38.842 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:38.846 return
2025-07-01 03:05:38.850 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:38.855 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:38.859 else:
2025-07-01 03:05:38.863 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:38.867 eqi = None
2025-07-01 03:05:38.872
2025-07-01 03:05:38.876 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:38.880 # identical
2025-07-01 03:05:38.884
2025-07-01 03:05:38.889 # pump out diffs from before the synch point
2025-07-01 03:05:38.893 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:38.897
2025-07-01 03:05:38.902 # do intraline marking on the synch pair
2025-07-01 03:05:38.906 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:38.910 if eqi is None:
2025-07-01 03:05:38.914 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:38.919 atags = btags = ""
2025-07-01 03:05:38.923 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:38.927 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:38.931 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:38.936 if tag == 'replace':
2025-07-01 03:05:38.940 atags += '^' * la
2025-07-01 03:05:38.944 btags += '^' * lb
2025-07-01 03:05:38.949 elif tag == 'delete':
2025-07-01 03:05:38.953 atags += '-' * la
2025-07-01 03:05:38.958 elif tag == 'insert':
2025-07-01 03:05:38.962 btags += '+' * lb
2025-07-01 03:05:38.967 elif tag == 'equal':
2025-07-01 03:05:38.971 atags += ' ' * la
2025-07-01 03:05:38.975 btags += ' ' * lb
2025-07-01 03:05:38.980 else:
2025-07-01 03:05:38.984 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:38.988 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:38.993 else:
2025-07-01 03:05:38.997 # the synch pair is identical
2025-07-01 03:05:39.002 yield '  ' + aelt
2025-07-01 03:05:39.006
2025-07-01 03:05:39.010 # pump out diffs from after the synch point
2025-07-01 03:05:39.015 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:39.019
2025-07-01 03:05:39.023 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:39.027 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:39.032
2025-07-01 03:05:39.036 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:39.041 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:39.046 alo = 471, ahi = 1101
2025-07-01 03:05:39.050 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:39.055 blo = 471, bhi = 1101
2025-07-01 03:05:39.059
2025-07-01 03:05:39.063 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:39.068 g = []
2025-07-01 03:05:39.072 if alo < ahi:
2025-07-01 03:05:39.078 if blo < bhi:
2025-07-01 03:05:39.083 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:39.088 else:
2025-07-01 03:05:39.094 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:39.098 elif blo < bhi:
2025-07-01 03:05:39.103 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:39.107
2025-07-01 03:05:39.111 >       yield from g
2025-07-01 03:05:39.116
2025-07-01 03:05:39.120 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:39.124 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:39.128
2025-07-01 03:05:39.133 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:39.138 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:39.142 alo = 471, ahi = 1101
2025-07-01 03:05:39.147 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:39.151 blo = 471, bhi = 1101
2025-07-01 03:05:39.155
2025-07-01 03:05:39.160 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:39.164 r"""
2025-07-01 03:05:39.168 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:39.173 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:39.177 synch point, and intraline difference marking is done on the
2025-07-01 03:05:39.181 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:39.186
2025-07-01 03:05:39.190 Example:
2025-07-01 03:05:39.194
2025-07-01 03:05:39.198 >>> d = Differ()
2025-07-01 03:05:39.203 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:39.207 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:39.212 >>> print(''.join(results), end="")
2025-07-01 03:05:39.216 - abcDefghiJkl
2025-07-01 03:05:39.225 + abcdefGhijkl
2025-07-01 03:05:39.233 """
2025-07-01 03:05:39.237
2025-07-01 03:05:39.242 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:39.247 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:39.252 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:39.258 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:39.263 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:39.267
2025-07-01 03:05:39.271 # search for the pair that matches best without being identical
2025-07-01 03:05:39.276 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:39.280 # on junk -- unless we have to)
2025-07-01 03:05:39.284 for j in range(blo, bhi):
2025-07-01 03:05:39.288 bj = b[j]
2025-07-01 03:05:39.293 cruncher.set_seq2(bj)
2025-07-01 03:05:39.297 for i in range(alo, ahi):
2025-07-01 03:05:39.301 ai = a[i]
2025-07-01 03:05:39.306 if ai == bj:
2025-07-01 03:05:39.310 if eqi is None:
2025-07-01 03:05:39.314 eqi, eqj = i, j
2025-07-01 03:05:39.319 continue
2025-07-01 03:05:39.323 cruncher.set_seq1(ai)
2025-07-01 03:05:39.327 # computing similarity is expensive, so use the quick
2025-07-01 03:05:39.333 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:39.337 # compares by a factor of 3.
2025-07-01 03:05:39.341 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:39.346 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:39.350 # of the computation is cached by cruncher
2025-07-01 03:05:39.355 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:39.359 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:39.364 cruncher.ratio() > best_ratio:
2025-07-01 03:05:39.368 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:39.373 if best_ratio < cutoff:
2025-07-01 03:05:39.377 # no non-identical "pretty close" pair
2025-07-01 03:05:39.381 if eqi is None:
2025-07-01 03:05:39.386 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:39.390 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:39.395 return
2025-07-01 03:05:39.399 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:39.403 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:39.408 else:
2025-07-01 03:05:39.412 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:39.416 eqi = None
2025-07-01 03:05:39.420
2025-07-01 03:05:39.425 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:39.429 # identical
2025-07-01 03:05:39.434
2025-07-01 03:05:39.438 # pump out diffs from before the synch point
2025-07-01 03:05:39.443 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:39.447
2025-07-01 03:05:39.452 # do intraline marking on the synch pair
2025-07-01 03:05:39.457 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:39.461 if eqi is None:
2025-07-01 03:05:39.465 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:39.469 atags = btags = ""
2025-07-01 03:05:39.474 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:39.478 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:39.482 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:39.487 if tag == 'replace':
2025-07-01 03:05:39.491 atags += '^' * la
2025-07-01 03:05:39.495 btags += '^' * lb
2025-07-01 03:05:39.500 elif tag == 'delete':
2025-07-01 03:05:39.504 atags += '-' * la
2025-07-01 03:05:39.508 elif tag == 'insert':
2025-07-01 03:05:39.513 btags += '+' * lb
2025-07-01 03:05:39.517 elif tag == 'equal':
2025-07-01 03:05:39.522 atags += ' ' * la
2025-07-01 03:05:39.526 btags += ' ' * lb
2025-07-01 03:05:39.531 else:
2025-07-01 03:05:39.535 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:39.539 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:39.544 else:
2025-07-01 03:05:39.548 # the synch pair is identical
2025-07-01 03:05:39.552 yield '  ' + aelt
2025-07-01 03:05:39.557
2025-07-01 03:05:39.561 # pump out diffs from after the synch point
2025-07-01 03:05:39.565 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:39.570
2025-07-01 03:05:39.574 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:39.579 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:39.583
2025-07-01 03:05:39.587 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:39.592 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:39.596 alo = 472, ahi = 1101
2025-07-01 03:05:39.602 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:39.607 blo = 472, bhi = 1101
2025-07-01 03:05:39.611
2025-07-01 03:05:39.616 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:39.621 g = []
2025-07-01 03:05:39.625 if alo < ahi:
2025-07-01 03:05:39.630 if blo < bhi:
2025-07-01 03:05:39.635 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:39.640 else:
2025-07-01 03:05:39.644 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:39.649 elif blo < bhi:
2025-07-01 03:05:39.654 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:39.658
2025-07-01 03:05:39.662 >       yield from g
2025-07-01 03:05:39.667
2025-07-01 03:05:39.671 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:39.677 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:39.681
2025-07-01 03:05:39.686 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:39.691 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:39.695 alo = 472, ahi = 1101
2025-07-01 03:05:39.701 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:39.705 blo = 472, bhi = 1101
2025-07-01 03:05:39.710
2025-07-01 03:05:39.714 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:39.720 r"""
2025-07-01 03:05:39.725 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:39.729 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:39.734 synch point, and intraline difference marking is done on the
2025-07-01 03:05:39.738 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:39.742
2025-07-01 03:05:39.747 Example:
2025-07-01 03:05:39.751
2025-07-01 03:05:39.755 >>> d = Differ()
2025-07-01 03:05:39.760 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:39.764 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:39.769 >>> print(''.join(results), end="")
2025-07-01 03:05:39.773 - abcDefghiJkl
2025-07-01 03:05:39.783 + abcdefGhijkl
2025-07-01 03:05:39.792 """
2025-07-01 03:05:39.797
2025-07-01 03:05:39.801 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:39.806 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:39.810 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:39.815 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:39.819 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:39.824
2025-07-01 03:05:39.829 # search for the pair that matches best without being identical
2025-07-01 03:05:39.834 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:39.839 # on junk -- unless we have to)
2025-07-01 03:05:39.843 for j in range(blo, bhi):
2025-07-01 03:05:39.848 bj = b[j]
2025-07-01 03:05:39.852 cruncher.set_seq2(bj)
2025-07-01 03:05:39.857 for i in range(alo, ahi):
2025-07-01 03:05:39.861 ai = a[i]
2025-07-01 03:05:39.866 if ai == bj:
2025-07-01 03:05:39.871 if eqi is None:
2025-07-01 03:05:39.876 eqi, eqj = i, j
2025-07-01 03:05:39.880 continue
2025-07-01 03:05:39.885 cruncher.set_seq1(ai)
2025-07-01 03:05:39.889 # computing similarity is expensive, so use the quick
2025-07-01 03:05:39.894 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:39.899 # compares by a factor of 3.
2025-07-01 03:05:39.903 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:39.908 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:39.912 # of the computation is cached by cruncher
2025-07-01 03:05:39.917 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:39.923 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:39.930 cruncher.ratio() > best_ratio:
2025-07-01 03:05:39.936 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:39.943 if best_ratio < cutoff:
2025-07-01 03:05:39.950 # no non-identical "pretty close" pair
2025-07-01 03:05:39.956 if eqi is None:
2025-07-01 03:05:39.962 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:39.968 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:39.973 return
2025-07-01 03:05:39.978 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:39.982 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:39.987 else:
2025-07-01 03:05:39.991 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:39.996 eqi = None
2025-07-01 03:05:40.000
2025-07-01 03:05:40.005 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:40.011 # identical
2025-07-01 03:05:40.016
2025-07-01 03:05:40.021 # pump out diffs from before the synch point
2025-07-01 03:05:40.026 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:40.031
2025-07-01 03:05:40.036 # do intraline marking on the synch pair
2025-07-01 03:05:40.042 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:40.047 if eqi is None:
2025-07-01 03:05:40.052 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:40.058 atags = btags = ""
2025-07-01 03:05:40.064 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:40.069 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:40.074 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:40.079 if tag == 'replace':
2025-07-01 03:05:40.084 atags += '^' * la
2025-07-01 03:05:40.089 btags += '^' * lb
2025-07-01 03:05:40.094 elif tag == 'delete':
2025-07-01 03:05:40.098 atags += '-' * la
2025-07-01 03:05:40.103 elif tag == 'insert':
2025-07-01 03:05:40.108 btags += '+' * lb
2025-07-01 03:05:40.114 elif tag == 'equal':
2025-07-01 03:05:40.119 atags += ' ' * la
2025-07-01 03:05:40.124 btags += ' ' * lb
2025-07-01 03:05:40.129 else:
2025-07-01 03:05:40.134 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:40.139 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:40.144 else:
2025-07-01 03:05:40.148 # the synch pair is identical
2025-07-01 03:05:40.153 yield '  ' + aelt
2025-07-01 03:05:40.157
2025-07-01 03:05:40.162 # pump out diffs from after the synch point
2025-07-01 03:05:40.167 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:40.172
2025-07-01 03:05:40.176 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:40.181 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:40.185
2025-07-01 03:05:40.190 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:40.194 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:40.199 alo = 473, ahi = 1101
2025-07-01 03:05:40.203 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:40.208 blo = 473, bhi = 1101
2025-07-01 03:05:40.212
2025-07-01 03:05:40.216 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:40.221 g = []
2025-07-01 03:05:40.225 if alo < ahi:
2025-07-01 03:05:40.230 if blo < bhi:
2025-07-01 03:05:40.234 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:40.239 else:
2025-07-01 03:05:40.243 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:40.248 elif blo < bhi:
2025-07-01 03:05:40.252 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:40.256
2025-07-01 03:05:40.261 >       yield from g
2025-07-01 03:05:40.266
2025-07-01 03:05:40.271 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:40.277 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:40.281
2025-07-01 03:05:40.285 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:40.290 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:40.295 alo = 473, ahi = 1101
2025-07-01 03:05:40.300 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:40.305 blo = 473, bhi = 1101
2025-07-01 03:05:40.310
2025-07-01 03:05:40.315 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:40.319 r"""
2025-07-01 03:05:40.324 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:40.328 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:40.333 synch point, and intraline difference marking is done on the
2025-07-01 03:05:40.337 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:40.342
2025-07-01 03:05:40.346 Example:
2025-07-01 03:05:40.350
2025-07-01 03:05:40.355 >>> d = Differ()
2025-07-01 03:05:40.359 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:40.364 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:40.368 >>> print(''.join(results), end="")
2025-07-01 03:05:40.373 - abcDefghiJkl
2025-07-01 03:05:40.383 + abcdefGhijkl
2025-07-01 03:05:40.393 """
2025-07-01 03:05:40.397
2025-07-01 03:05:40.402 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:40.407 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:40.412 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:40.417 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:40.422 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:40.426
2025-07-01 03:05:40.431 # search for the pair that matches best without being identical
2025-07-01 03:05:40.436 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:40.440 # on junk -- unless we have to)
2025-07-01 03:05:40.445 for j in range(blo, bhi):
2025-07-01 03:05:40.449 bj = b[j]
2025-07-01 03:05:40.454 cruncher.set_seq2(bj)
2025-07-01 03:05:40.458 for i in range(alo, ahi):
2025-07-01 03:05:40.463 ai = a[i]
2025-07-01 03:05:40.467 if ai == bj:
2025-07-01 03:05:40.472 if eqi is None:
2025-07-01 03:05:40.478 eqi, eqj = i, j
2025-07-01 03:05:40.482 continue
2025-07-01 03:05:40.487 cruncher.set_seq1(ai)
2025-07-01 03:05:40.492 # computing similarity is expensive, so use the quick
2025-07-01 03:05:40.496 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:40.501 # compares by a factor of 3.
2025-07-01 03:05:40.506 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:40.511 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:40.515 # of the computation is cached by cruncher
2025-07-01 03:05:40.520 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:40.524 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:40.529 cruncher.ratio() > best_ratio:
2025-07-01 03:05:40.533 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:40.537 if best_ratio < cutoff:
2025-07-01 03:05:40.542 # no non-identical "pretty close" pair
2025-07-01 03:05:40.547 if eqi is None:
2025-07-01 03:05:40.551 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:40.556 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:40.560 return
2025-07-01 03:05:40.565 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:40.570 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:40.574 else:
2025-07-01 03:05:40.580 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:40.585 eqi = None
2025-07-01 03:05:40.589
2025-07-01 03:05:40.593 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:40.598 # identical
2025-07-01 03:05:40.602
2025-07-01 03:05:40.607 # pump out diffs from before the synch point
2025-07-01 03:05:40.611 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:40.616
2025-07-01 03:05:40.620 # do intraline marking on the synch pair
2025-07-01 03:05:40.625 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:40.629 if eqi is None:
2025-07-01 03:05:40.634 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:40.638 atags = btags = ""
2025-07-01 03:05:40.642 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:40.647 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:40.652 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:40.656 if tag == 'replace':
2025-07-01 03:05:40.661 atags += '^' * la
2025-07-01 03:05:40.665 btags += '^' * lb
2025-07-01 03:05:40.670 elif tag == 'delete':
2025-07-01 03:05:40.675 atags += '-' * la
2025-07-01 03:05:40.680 elif tag == 'insert':
2025-07-01 03:05:40.685 btags += '+' * lb
2025-07-01 03:05:40.690 elif tag == 'equal':
2025-07-01 03:05:40.694 atags += ' ' * la
2025-07-01 03:05:40.698 btags += ' ' * lb
2025-07-01 03:05:40.703 else:
2025-07-01 03:05:40.707 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:40.712 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:40.716 else:
2025-07-01 03:05:40.721 # the synch pair is identical
2025-07-01 03:05:40.725 yield '  ' + aelt
2025-07-01 03:05:40.729
2025-07-01 03:05:40.734 # pump out diffs from after the synch point
2025-07-01 03:05:40.738 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:40.743
2025-07-01 03:05:40.747 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:40.751 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:40.756
2025-07-01 03:05:40.760 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:40.765 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:40.770 alo = 474, ahi = 1101
2025-07-01 03:05:40.774 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:40.779 blo = 474, bhi = 1101
2025-07-01 03:05:40.784
2025-07-01 03:05:40.788 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:40.793 g = []
2025-07-01 03:05:40.797 if alo < ahi:
2025-07-01 03:05:40.802 if blo < bhi:
2025-07-01 03:05:40.806 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:40.811 else:
2025-07-01 03:05:40.815 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:40.819 elif blo < bhi:
2025-07-01 03:05:40.824 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:40.828
2025-07-01 03:05:40.832 >       yield from g
2025-07-01 03:05:40.837
2025-07-01 03:05:40.841 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:40.845 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:40.850
2025-07-01 03:05:40.855 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:40.859 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:40.864 alo = 474, ahi = 1101
2025-07-01 03:05:40.869 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:40.873 blo = 474, bhi = 1101
2025-07-01 03:05:40.878
2025-07-01 03:05:40.883 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:40.888 r"""
2025-07-01 03:05:40.893 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:40.898 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:40.903 synch point, and intraline difference marking is done on the
2025-07-01 03:05:40.908 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:40.913
2025-07-01 03:05:40.917 Example:
2025-07-01 03:05:40.922
2025-07-01 03:05:40.926 >>> d = Differ()
2025-07-01 03:05:40.930 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:40.936 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:40.940 >>> print(''.join(results), end="")
2025-07-01 03:05:40.945 - abcDefghiJkl
2025-07-01 03:05:40.954 + abcdefGhijkl
2025-07-01 03:05:40.962 """
2025-07-01 03:05:40.966
2025-07-01 03:05:40.971 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:40.975 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:40.980 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:40.984 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:40.989 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:40.993
2025-07-01 03:05:40.998 # search for the pair that matches best without being identical
2025-07-01 03:05:41.003 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:41.007 # on junk -- unless we have to)
2025-07-01 03:05:41.011 for j in range(blo, bhi):
2025-07-01 03:05:41.016 bj = b[j]
2025-07-01 03:05:41.020 cruncher.set_seq2(bj)
2025-07-01 03:05:41.025 for i in range(alo, ahi):
2025-07-01 03:05:41.029 ai = a[i]
2025-07-01 03:05:41.034 if ai == bj:
2025-07-01 03:05:41.038 if eqi is None:
2025-07-01 03:05:41.043 eqi, eqj = i, j
2025-07-01 03:05:41.047 continue
2025-07-01 03:05:41.051 cruncher.set_seq1(ai)
2025-07-01 03:05:41.056 # computing similarity is expensive, so use the quick
2025-07-01 03:05:41.061 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:41.065 # compares by a factor of 3.
2025-07-01 03:05:41.070 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:41.074 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:41.079 # of the computation is cached by cruncher
2025-07-01 03:05:41.084 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:41.088 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:41.093 cruncher.ratio() > best_ratio:
2025-07-01 03:05:41.098 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:41.103 if best_ratio < cutoff:
2025-07-01 03:05:41.107 # no non-identical "pretty close" pair
2025-07-01 03:05:41.112 if eqi is None:
2025-07-01 03:05:41.117 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:41.121 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:41.125 return
2025-07-01 03:05:41.130 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:41.134 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:41.139 else:
2025-07-01 03:05:41.144 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:41.149 eqi = None
2025-07-01 03:05:41.153
2025-07-01 03:05:41.158 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:41.162 # identical
2025-07-01 03:05:41.167
2025-07-01 03:05:41.171 # pump out diffs from before the synch point
2025-07-01 03:05:41.176 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:41.181
2025-07-01 03:05:41.185 # do intraline marking on the synch pair
2025-07-01 03:05:41.189 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:41.194 if eqi is None:
2025-07-01 03:05:41.198 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:41.202 atags = btags = ""
2025-07-01 03:05:41.207 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:41.211 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:41.216 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:41.220 if tag == 'replace':
2025-07-01 03:05:41.224 atags += '^' * la
2025-07-01 03:05:41.229 btags += '^' * lb
2025-07-01 03:05:41.233 elif tag == 'delete':
2025-07-01 03:05:41.237 atags += '-' * la
2025-07-01 03:05:41.242 elif tag == 'insert':
2025-07-01 03:05:41.246 btags += '+' * lb
2025-07-01 03:05:41.250 elif tag == 'equal':
2025-07-01 03:05:41.255 atags += ' ' * la
2025-07-01 03:05:41.260 btags += ' ' * lb
2025-07-01 03:05:41.265 else:
2025-07-01 03:05:41.269 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:41.274 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:41.278 else:
2025-07-01 03:05:41.283 # the synch pair is identical
2025-07-01 03:05:41.287 yield '  ' + aelt
2025-07-01 03:05:41.292
2025-07-01 03:05:41.296 # pump out diffs from after the synch point
2025-07-01 03:05:41.301 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:41.305
2025-07-01 03:05:41.310 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:41.314 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:41.319
2025-07-01 03:05:41.323 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:41.328 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:41.333 alo = 475, ahi = 1101
2025-07-01 03:05:41.338 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:41.343 blo = 475, bhi = 1101
2025-07-01 03:05:41.349
2025-07-01 03:05:41.356 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:41.361 g = []
2025-07-01 03:05:41.366 if alo < ahi:
2025-07-01 03:05:41.370 if blo < bhi:
2025-07-01 03:05:41.374 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:41.379 else:
2025-07-01 03:05:41.383 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:41.388 elif blo < bhi:
2025-07-01 03:05:41.393 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:41.398
2025-07-01 03:05:41.402 >       yield from g
2025-07-01 03:05:41.407
2025-07-01 03:05:41.412 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:41.417 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:41.421
2025-07-01 03:05:41.426 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:41.430 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:41.435 alo = 475, ahi = 1101
2025-07-01 03:05:41.440 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:41.444 blo = 475, bhi = 1101
2025-07-01 03:05:41.448
2025-07-01 03:05:41.453 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:41.458 r"""
2025-07-01 03:05:41.462 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:41.467 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:41.471 synch point, and intraline difference marking is done on the
2025-07-01 03:05:41.476 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:41.480
2025-07-01 03:05:41.484 Example:
2025-07-01 03:05:41.488
2025-07-01 03:05:41.493 >>> d = Differ()
2025-07-01 03:05:41.497 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:41.502 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:41.506 >>> print(''.join(results), end="")
2025-07-01 03:05:41.511 - abcDefghiJkl
2025-07-01 03:05:41.519 + abcdefGhijkl
2025-07-01 03:05:41.528 """
2025-07-01 03:05:41.532
2025-07-01 03:05:41.536 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:41.541 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:41.545 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:41.550 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:41.554 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:41.559
2025-07-01 03:05:41.563 # search for the pair that matches best without being identical
2025-07-01 03:05:41.567 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:41.572 # on junk -- unless we have to)
2025-07-01 03:05:41.576 for j in range(blo, bhi):
2025-07-01 03:05:41.580 bj = b[j]
2025-07-01 03:05:41.585 cruncher.set_seq2(bj)
2025-07-01 03:05:41.589 for i in range(alo, ahi):
2025-07-01 03:05:41.594 ai = a[i]
2025-07-01 03:05:41.598 if ai == bj:
2025-07-01 03:05:41.603 if eqi is None:
2025-07-01 03:05:41.607 eqi, eqj = i, j
2025-07-01 03:05:41.611 continue
2025-07-01 03:05:41.616 cruncher.set_seq1(ai)
2025-07-01 03:05:41.620 # computing similarity is expensive, so use the quick
2025-07-01 03:05:41.625 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:41.629 # compares by a factor of 3.
2025-07-01 03:05:41.633 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:41.638 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:41.642 # of the computation is cached by cruncher
2025-07-01 03:05:41.647 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:41.651 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:41.656 cruncher.ratio() > best_ratio:
2025-07-01 03:05:41.660 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:41.664 if best_ratio < cutoff:
2025-07-01 03:05:41.669 # no non-identical "pretty close" pair
2025-07-01 03:05:41.673 if eqi is None:
2025-07-01 03:05:41.677 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:41.682 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:41.686 return
2025-07-01 03:05:41.691 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:41.695 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:41.699 else:
2025-07-01 03:05:41.704 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:41.708 eqi = None
2025-07-01 03:05:41.712
2025-07-01 03:05:41.717 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:41.721 # identical
2025-07-01 03:05:41.725
2025-07-01 03:05:41.730 # pump out diffs from before the synch point
2025-07-01 03:05:41.734 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:41.738
2025-07-01 03:05:41.743 # do intraline marking on the synch pair
2025-07-01 03:05:41.747 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:41.751 if eqi is None:
2025-07-01 03:05:41.756 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:41.760 atags = btags = ""
2025-07-01 03:05:41.764 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:41.769 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:41.773 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:41.777 if tag == 'replace':
2025-07-01 03:05:41.782 atags += '^' * la
2025-07-01 03:05:41.786 btags += '^' * lb
2025-07-01 03:05:41.790 elif tag == 'delete':
2025-07-01 03:05:41.795 atags += '-' * la
2025-07-01 03:05:41.799 elif tag == 'insert':
2025-07-01 03:05:41.803 btags += '+' * lb
2025-07-01 03:05:41.808 elif tag == 'equal':
2025-07-01 03:05:41.812 atags += ' ' * la
2025-07-01 03:05:41.816 btags += ' ' * lb
2025-07-01 03:05:41.821 else:
2025-07-01 03:05:41.825 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:41.830 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:41.834 else:
2025-07-01 03:05:41.838 # the synch pair is identical
2025-07-01 03:05:41.843 yield '  ' + aelt
2025-07-01 03:05:41.847
2025-07-01 03:05:41.852 # pump out diffs from after the synch point
2025-07-01 03:05:41.856 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:41.860
2025-07-01 03:05:41.865 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:41.869 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:41.873
2025-07-01 03:05:41.878 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:41.882 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:41.887 alo = 476, ahi = 1101
2025-07-01 03:05:41.892 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:41.896 blo = 476, bhi = 1101
2025-07-01 03:05:41.900
2025-07-01 03:05:41.904 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:41.909 g = []
2025-07-01 03:05:41.913 if alo < ahi:
2025-07-01 03:05:41.917 if blo < bhi:
2025-07-01 03:05:41.921 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:41.926 else:
2025-07-01 03:05:41.930 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:41.935 elif blo < bhi:
2025-07-01 03:05:41.939 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:41.943
2025-07-01 03:05:41.947 >       yield from g
2025-07-01 03:05:41.952
2025-07-01 03:05:41.956 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:41.960 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:41.965
2025-07-01 03:05:41.969 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:41.974 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:41.979 alo = 476, ahi = 1101
2025-07-01 03:05:41.984 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:41.988 blo = 476, bhi = 1101
2025-07-01 03:05:41.992
2025-07-01 03:05:41.996 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:42.001 r"""
2025-07-01 03:05:42.006 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:42.011 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:42.015 synch point, and intraline difference marking is done on the
2025-07-01 03:05:42.020 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:42.025
2025-07-01 03:05:42.030 Example:
2025-07-01 03:05:42.035
2025-07-01 03:05:42.039 >>> d = Differ()
2025-07-01 03:05:42.044 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:42.049 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:42.054 >>> print(''.join(results), end="")
2025-07-01 03:05:42.058 - abcDefghiJkl
2025-07-01 03:05:42.068 + abcdefGhijkl
2025-07-01 03:05:42.077 """
2025-07-01 03:05:42.081
2025-07-01 03:05:42.086 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:42.090 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:42.094 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:42.100 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:42.104 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:42.108
2025-07-01 03:05:42.113 # search for the pair that matches best without being identical
2025-07-01 03:05:42.117 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:42.122 # on junk -- unless we have to)
2025-07-01 03:05:42.126 for j in range(blo, bhi):
2025-07-01 03:05:42.130 bj = b[j]
2025-07-01 03:05:42.135 cruncher.set_seq2(bj)
2025-07-01 03:05:42.139 for i in range(alo, ahi):
2025-07-01 03:05:42.144 ai = a[i]
2025-07-01 03:05:42.148 if ai == bj:
2025-07-01 03:05:42.152 if eqi is None:
2025-07-01 03:05:42.157 eqi, eqj = i, j
2025-07-01 03:05:42.161 continue
2025-07-01 03:05:42.165 cruncher.set_seq1(ai)
2025-07-01 03:05:42.170 # computing similarity is expensive, so use the quick
2025-07-01 03:05:42.176 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:42.180 # compares by a factor of 3.
2025-07-01 03:05:42.185 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:42.190 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:42.194 # of the computation is cached by cruncher
2025-07-01 03:05:42.199 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:42.203 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:42.208 cruncher.ratio() > best_ratio:
2025-07-01 03:05:42.212 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:42.217 if best_ratio < cutoff:
2025-07-01 03:05:42.221 # no non-identical "pretty close" pair
2025-07-01 03:05:42.226 if eqi is None:
2025-07-01 03:05:42.230 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:42.235 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:42.239 return
2025-07-01 03:05:42.245 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:42.249 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:42.254 else:
2025-07-01 03:05:42.259 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:42.263 eqi = None
2025-07-01 03:05:42.268
2025-07-01 03:05:42.272 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:42.277 # identical
2025-07-01 03:05:42.281
2025-07-01 03:05:42.286 # pump out diffs from before the synch point
2025-07-01 03:05:42.290 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:42.294
2025-07-01 03:05:42.298 # do intraline marking on the synch pair
2025-07-01 03:05:42.303 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:42.307 if eqi is None:
2025-07-01 03:05:42.312 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:42.316 atags = btags = ""
2025-07-01 03:05:42.321 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:42.325 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:42.330 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:42.334 if tag == 'replace':
2025-07-01 03:05:42.339 atags += '^' * la
2025-07-01 03:05:42.343 btags += '^' * lb
2025-07-01 03:05:42.348 elif tag == 'delete':
2025-07-01 03:05:42.352 atags += '-' * la
2025-07-01 03:05:42.356 elif tag == 'insert':
2025-07-01 03:05:42.361 btags += '+' * lb
2025-07-01 03:05:42.365 elif tag == 'equal':
2025-07-01 03:05:42.369 atags += ' ' * la
2025-07-01 03:05:42.374 btags += ' ' * lb
2025-07-01 03:05:42.378 else:
2025-07-01 03:05:42.382 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:42.387 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:42.391 else:
2025-07-01 03:05:42.396 # the synch pair is identical
2025-07-01 03:05:42.400 yield '  ' + aelt
2025-07-01 03:05:42.404
2025-07-01 03:05:42.409 # pump out diffs from after the synch point
2025-07-01 03:05:42.413 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:42.418
2025-07-01 03:05:42.423 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:42.427 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:42.432
2025-07-01 03:05:42.438 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:42.442 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:42.446 alo = 477, ahi = 1101
2025-07-01 03:05:42.451 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:42.456 blo = 477, bhi = 1101
2025-07-01 03:05:42.460
2025-07-01 03:05:42.465 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:42.469 g = []
2025-07-01 03:05:42.473 if alo < ahi:
2025-07-01 03:05:42.478 if blo < bhi:
2025-07-01 03:05:42.482 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:42.487 else:
2025-07-01 03:05:42.491 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:42.496 elif blo < bhi:
2025-07-01 03:05:42.500 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:42.505
2025-07-01 03:05:42.509 >       yield from g
2025-07-01 03:05:42.513
2025-07-01 03:05:42.518 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:42.522 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:42.526
2025-07-01 03:05:42.531 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:42.535 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:42.540 alo = 477, ahi = 1101
2025-07-01 03:05:42.545 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:42.550 blo = 477, bhi = 1101
2025-07-01 03:05:42.554
2025-07-01 03:05:42.558 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:42.563 r"""
2025-07-01 03:05:42.568 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:42.572 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:42.577 synch point, and intraline difference marking is done on the
2025-07-01 03:05:42.581 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:42.586
2025-07-01 03:05:42.590 Example:
2025-07-01 03:05:42.594
2025-07-01 03:05:42.599 >>> d = Differ()
2025-07-01 03:05:42.603 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:42.608 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:42.613 >>> print(''.join(results), end="")
2025-07-01 03:05:42.619 - abcDefghiJkl
2025-07-01 03:05:42.630 + abcdefGhijkl
2025-07-01 03:05:42.641 """
2025-07-01 03:05:42.645
2025-07-01 03:05:42.651 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:42.656 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:42.661 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:42.665 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:42.669 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:42.674
2025-07-01 03:05:42.678 # search for the pair that matches best without being identical
2025-07-01 03:05:42.683 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:42.689 # on junk -- unless we have to)
2025-07-01 03:05:42.695 for j in range(blo, bhi):
2025-07-01 03:05:42.701 bj = b[j]
2025-07-01 03:05:42.707 cruncher.set_seq2(bj)
2025-07-01 03:05:42.712 for i in range(alo, ahi):
2025-07-01 03:05:42.716 ai = a[i]
2025-07-01 03:05:42.721 if ai == bj:
2025-07-01 03:05:42.725 if eqi is None:
2025-07-01 03:05:42.730 eqi, eqj = i, j
2025-07-01 03:05:42.734 continue
2025-07-01 03:05:42.739 cruncher.set_seq1(ai)
2025-07-01 03:05:42.744 # computing similarity is expensive, so use the quick
2025-07-01 03:05:42.748 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:42.753 # compares by a factor of 3.
2025-07-01 03:05:42.758 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:42.762 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:42.767 # of the computation is cached by cruncher
2025-07-01 03:05:42.771 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:42.776 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:42.780 cruncher.ratio() > best_ratio:
2025-07-01 03:05:42.785 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:42.789 if best_ratio < cutoff:
2025-07-01 03:05:42.794 # no non-identical "pretty close" pair
2025-07-01 03:05:42.798 if eqi is None:
2025-07-01 03:05:42.803 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:42.807 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:42.812 return
2025-07-01 03:05:42.817 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:42.821 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:42.825 else:
2025-07-01 03:05:42.830 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:42.835 eqi = None
2025-07-01 03:05:42.840
2025-07-01 03:05:42.846 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:42.853 # identical
2025-07-01 03:05:42.859
2025-07-01 03:05:42.866 # pump out diffs from before the synch point
2025-07-01 03:05:42.872 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:42.878
2025-07-01 03:05:42.883 # do intraline marking on the synch pair
2025-07-01 03:05:42.887 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:42.891 if eqi is None:
2025-07-01 03:05:42.895 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:42.900 atags = btags = ""
2025-07-01 03:05:42.904 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:42.908 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:42.913 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:42.917 if tag == 'replace':
2025-07-01 03:05:42.921 atags += '^' * la
2025-07-01 03:05:42.926 btags += '^' * lb
2025-07-01 03:05:42.930 elif tag == 'delete':
2025-07-01 03:05:42.934 atags += '-' * la
2025-07-01 03:05:42.939 elif tag == 'insert':
2025-07-01 03:05:42.943 btags += '+' * lb
2025-07-01 03:05:42.947 elif tag == 'equal':
2025-07-01 03:05:42.951 atags += ' ' * la
2025-07-01 03:05:42.955 btags += ' ' * lb
2025-07-01 03:05:42.959 else:
2025-07-01 03:05:42.964 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:42.968 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:42.972 else:
2025-07-01 03:05:42.976 # the synch pair is identical
2025-07-01 03:05:42.980 yield '  ' + aelt
2025-07-01 03:05:42.984
2025-07-01 03:05:42.989 # pump out diffs from after the synch point
2025-07-01 03:05:42.993 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:42.997
2025-07-01 03:05:43.001 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:43.006 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:43.010
2025-07-01 03:05:43.014 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:43.019 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:43.023 alo = 478, ahi = 1101
2025-07-01 03:05:43.028 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:43.032 blo = 478, bhi = 1101
2025-07-01 03:05:43.036
2025-07-01 03:05:43.040 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:43.044 g = []
2025-07-01 03:05:43.049 if alo < ahi:
2025-07-01 03:05:43.053 if blo < bhi:
2025-07-01 03:05:43.057 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:43.062 else:
2025-07-01 03:05:43.066 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:43.070 elif blo < bhi:
2025-07-01 03:05:43.075 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:43.079
2025-07-01 03:05:43.083 >       yield from g
2025-07-01 03:05:43.087
2025-07-01 03:05:43.092 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:43.096 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:43.100
2025-07-01 03:05:43.104 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:43.109 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:43.113 alo = 478, ahi = 1101
2025-07-01 03:05:43.118 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:43.122 blo = 478, bhi = 1101
2025-07-01 03:05:43.126
2025-07-01 03:05:43.131 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:43.135 r"""
2025-07-01 03:05:43.139 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:43.144 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:43.148 synch point, and intraline difference marking is done on the
2025-07-01 03:05:43.152 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:43.156
2025-07-01 03:05:43.160 Example:
2025-07-01 03:05:43.164
2025-07-01 03:05:43.169 >>> d = Differ()
2025-07-01 03:05:43.173 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:43.177 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:43.181 >>> print(''.join(results), end="")
2025-07-01 03:05:43.186 - abcDefghiJkl
2025-07-01 03:05:43.194 + abcdefGhijkl
2025-07-01 03:05:43.204 """
2025-07-01 03:05:43.208
2025-07-01 03:05:43.212 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:43.217 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:43.221 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:43.225 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:43.230 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:43.234
2025-07-01 03:05:43.238 # search for the pair that matches best without being identical
2025-07-01 03:05:43.242 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:43.247 # on junk -- unless we have to)
2025-07-01 03:05:43.251 for j in range(blo, bhi):
2025-07-01 03:05:43.255 bj = b[j]
2025-07-01 03:05:43.259 cruncher.set_seq2(bj)
2025-07-01 03:05:43.263 for i in range(alo, ahi):
2025-07-01 03:05:43.268 ai = a[i]
2025-07-01 03:05:43.272 if ai == bj:
2025-07-01 03:05:43.276 if eqi is None:
2025-07-01 03:05:43.280 eqi, eqj = i, j
2025-07-01 03:05:43.285 continue
2025-07-01 03:05:43.289 cruncher.set_seq1(ai)
2025-07-01 03:05:43.293 # computing similarity is expensive, so use the quick
2025-07-01 03:05:43.298 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:43.302 # compares by a factor of 3.
2025-07-01 03:05:43.306 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:43.311 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:43.315 # of the computation is cached by cruncher
2025-07-01 03:05:43.319 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:43.323 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:43.328 cruncher.ratio() > best_ratio:
2025-07-01 03:05:43.332 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:43.336 if best_ratio < cutoff:
2025-07-01 03:05:43.340 # no non-identical "pretty close" pair
2025-07-01 03:05:43.345 if eqi is None:
2025-07-01 03:05:43.349 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:43.353 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:43.357 return
2025-07-01 03:05:43.362 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:43.366 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:43.370 else:
2025-07-01 03:05:43.374 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:43.379 eqi = None
2025-07-01 03:05:43.383
2025-07-01 03:05:43.387 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:43.391 # identical
2025-07-01 03:05:43.396
2025-07-01 03:05:43.400 # pump out diffs from before the synch point
2025-07-01 03:05:43.404 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:43.408
2025-07-01 03:05:43.413 # do intraline marking on the synch pair
2025-07-01 03:05:43.417 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:43.421 if eqi is None:
2025-07-01 03:05:43.425 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:43.430 atags = btags = ""
2025-07-01 03:05:43.434 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:43.438 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:43.443 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:43.447 if tag == 'replace':
2025-07-01 03:05:43.451 atags += '^' * la
2025-07-01 03:05:43.455 btags += '^' * lb
2025-07-01 03:05:43.459 elif tag == 'delete':
2025-07-01 03:05:43.464 atags += '-' * la
2025-07-01 03:05:43.468 elif tag == 'insert':
2025-07-01 03:05:43.472 btags += '+' * lb
2025-07-01 03:05:43.476 elif tag == 'equal':
2025-07-01 03:05:43.481 atags += ' ' * la
2025-07-01 03:05:43.485 btags += ' ' * lb
2025-07-01 03:05:43.489 else:
2025-07-01 03:05:43.493 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:43.498 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:43.502 else:
2025-07-01 03:05:43.506 # the synch pair is identical
2025-07-01 03:05:43.510 yield '  ' + aelt
2025-07-01 03:05:43.514
2025-07-01 03:05:43.519 # pump out diffs from after the synch point
2025-07-01 03:05:43.523 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:43.527
2025-07-01 03:05:43.531 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:43.536 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:43.540
2025-07-01 03:05:43.544 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:43.549 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:43.553 alo = 479, ahi = 1101
2025-07-01 03:05:43.558 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:43.562 blo = 479, bhi = 1101
2025-07-01 03:05:43.566
2025-07-01 03:05:43.570 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:43.574 g = []
2025-07-01 03:05:43.578 if alo < ahi:
2025-07-01 03:05:43.583 if blo < bhi:
2025-07-01 03:05:43.587 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:43.591 else:
2025-07-01 03:05:43.596 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:43.600 elif blo < bhi:
2025-07-01 03:05:43.604 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:43.608
2025-07-01 03:05:43.613 >       yield from g
2025-07-01 03:05:43.617
2025-07-01 03:05:43.621 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:43.626 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:43.630
2025-07-01 03:05:43.635 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:43.639 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:43.643 alo = 479, ahi = 1101
2025-07-01 03:05:43.648 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:43.652 blo = 479, bhi = 1101
2025-07-01 03:05:43.656
2025-07-01 03:05:43.661 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:43.665 r"""
2025-07-01 03:05:43.669 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:43.674 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:43.678 synch point, and intraline difference marking is done on the
2025-07-01 03:05:43.682 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:43.686
2025-07-01 03:05:43.690 Example:
2025-07-01 03:05:43.695
2025-07-01 03:05:43.699 >>> d = Differ()
2025-07-01 03:05:43.703 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:43.707 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:43.712 >>> print(''.join(results), end="")
2025-07-01 03:05:43.716 - abcDefghiJkl
2025-07-01 03:05:43.724 + abcdefGhijkl
2025-07-01 03:05:43.733 """
2025-07-01 03:05:43.737
2025-07-01 03:05:43.741 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:43.745 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:43.749 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:43.754 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:43.758 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:43.763
2025-07-01 03:05:43.767 # search for the pair that matches best without being identical
2025-07-01 03:05:43.771 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:43.775 # on junk -- unless we have to)
2025-07-01 03:05:43.780 for j in range(blo, bhi):
2025-07-01 03:05:43.784 bj = b[j]
2025-07-01 03:05:43.788 cruncher.set_seq2(bj)
2025-07-01 03:05:43.793 for i in range(alo, ahi):
2025-07-01 03:05:43.797 ai = a[i]
2025-07-01 03:05:43.801 if ai == bj:
2025-07-01 03:05:43.805 if eqi is None:
2025-07-01 03:05:43.810 eqi, eqj = i, j
2025-07-01 03:05:43.814 continue
2025-07-01 03:05:43.818 cruncher.set_seq1(ai)
2025-07-01 03:05:43.823 # computing similarity is expensive, so use the quick
2025-07-01 03:05:43.828 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:43.832 # compares by a factor of 3.
2025-07-01 03:05:43.837 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:43.842 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:43.846 # of the computation is cached by cruncher
2025-07-01 03:05:43.851 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:43.855 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:43.860 cruncher.ratio() > best_ratio:
2025-07-01 03:05:43.864 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:43.869 if best_ratio < cutoff:
2025-07-01 03:05:43.873 # no non-identical "pretty close" pair
2025-07-01 03:05:43.878 if eqi is None:
2025-07-01 03:05:43.882 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:43.887 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:43.891 return
2025-07-01 03:05:43.895 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:43.900 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:43.904 else:
2025-07-01 03:05:43.908 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:43.913 eqi = None
2025-07-01 03:05:43.917
2025-07-01 03:05:43.921 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:43.926 # identical
2025-07-01 03:05:43.930
2025-07-01 03:05:43.934 # pump out diffs from before the synch point
2025-07-01 03:05:43.939 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:43.943
2025-07-01 03:05:43.947 # do intraline marking on the synch pair
2025-07-01 03:05:43.951 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:43.956 if eqi is None:
2025-07-01 03:05:43.960 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:43.964 atags = btags = ""
2025-07-01 03:05:43.969 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:43.973 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:43.977 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:43.982 if tag == 'replace':
2025-07-01 03:05:43.986 atags += '^' * la
2025-07-01 03:05:43.990 btags += '^' * lb
2025-07-01 03:05:43.994 elif tag == 'delete':
2025-07-01 03:05:43.999 atags += '-' * la
2025-07-01 03:05:44.004 elif tag == 'insert':
2025-07-01 03:05:44.008 btags += '+' * lb
2025-07-01 03:05:44.012 elif tag == 'equal':
2025-07-01 03:05:44.017 atags += ' ' * la
2025-07-01 03:05:44.021 btags += ' ' * lb
2025-07-01 03:05:44.025 else:
2025-07-01 03:05:44.030 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:44.034 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:44.038 else:
2025-07-01 03:05:44.043 # the synch pair is identical
2025-07-01 03:05:44.047 yield '  ' + aelt
2025-07-01 03:05:44.051
2025-07-01 03:05:44.055 # pump out diffs from after the synch point
2025-07-01 03:05:44.060 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:44.064
2025-07-01 03:05:44.069 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:44.073 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:44.078
2025-07-01 03:05:44.082 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:44.087 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:44.091 alo = 480, ahi = 1101
2025-07-01 03:05:44.096 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:44.100 blo = 480, bhi = 1101
2025-07-01 03:05:44.105
2025-07-01 03:05:44.109 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:44.113 g = []
2025-07-01 03:05:44.118 if alo < ahi:
2025-07-01 03:05:44.122 if blo < bhi:
2025-07-01 03:05:44.127 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:44.131 else:
2025-07-01 03:05:44.136 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:44.140 elif blo < bhi:
2025-07-01 03:05:44.145 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:44.149
2025-07-01 03:05:44.153 >       yield from g
2025-07-01 03:05:44.157
2025-07-01 03:05:44.162 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:44.166 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:44.170
2025-07-01 03:05:44.175 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:44.179 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:44.184 alo = 480, ahi = 1101
2025-07-01 03:05:44.189 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:44.193 blo = 480, bhi = 1101
2025-07-01 03:05:44.197
2025-07-01 03:05:44.202 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:44.207 r"""
2025-07-01 03:05:44.211 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:44.215 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:44.220 synch point, and intraline difference marking is done on the
2025-07-01 03:05:44.225 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:44.230
2025-07-01 03:05:44.234 Example:
2025-07-01 03:05:44.238
2025-07-01 03:05:44.242 >>> d = Differ()
2025-07-01 03:05:44.246 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:44.251 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:44.255 >>> print(''.join(results), end="")
2025-07-01 03:05:44.259 - abcDefghiJkl
2025-07-01 03:05:44.268 + abcdefGhijkl
2025-07-01 03:05:44.276 """
2025-07-01 03:05:44.281
2025-07-01 03:05:44.285 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:44.289 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:44.294 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:44.298 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:44.302 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:44.307
2025-07-01 03:05:44.311 # search for the pair that matches best without being identical
2025-07-01 03:05:44.315 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:44.320 # on junk -- unless we have to)
2025-07-01 03:05:44.324 for j in range(blo, bhi):
2025-07-01 03:05:44.328 bj = b[j]
2025-07-01 03:05:44.333 cruncher.set_seq2(bj)
2025-07-01 03:05:44.337 for i in range(alo, ahi):
2025-07-01 03:05:44.341 ai = a[i]
2025-07-01 03:05:44.345 if ai == bj:
2025-07-01 03:05:44.349 if eqi is None:
2025-07-01 03:05:44.353 eqi, eqj = i, j
2025-07-01 03:05:44.358 continue
2025-07-01 03:05:44.362 cruncher.set_seq1(ai)
2025-07-01 03:05:44.366 # computing similarity is expensive, so use the quick
2025-07-01 03:05:44.371 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:44.375 # compares by a factor of 3.
2025-07-01 03:05:44.379 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:44.384 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:44.388 # of the computation is cached by cruncher
2025-07-01 03:05:44.393 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:44.397 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:44.402 cruncher.ratio() > best_ratio:
2025-07-01 03:05:44.406 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:44.410 if best_ratio < cutoff:
2025-07-01 03:05:44.415 # no non-identical "pretty close" pair
2025-07-01 03:05:44.419 if eqi is None:
2025-07-01 03:05:44.423 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:44.428 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:44.432 return
2025-07-01 03:05:44.437 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:44.441 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:44.446 else:
2025-07-01 03:05:44.450 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:44.454 eqi = None
2025-07-01 03:05:44.459
2025-07-01 03:05:44.463 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:44.468 # identical
2025-07-01 03:05:44.472
2025-07-01 03:05:44.476 # pump out diffs from before the synch point
2025-07-01 03:05:44.481 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:44.485
2025-07-01 03:05:44.490 # do intraline marking on the synch pair
2025-07-01 03:05:44.494 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:44.498 if eqi is None:
2025-07-01 03:05:44.503 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:44.507 atags = btags = ""
2025-07-01 03:05:44.512 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:44.516 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:44.521 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:44.525 if tag == 'replace':
2025-07-01 03:05:44.529 atags += '^' * la
2025-07-01 03:05:44.534 btags += '^' * lb
2025-07-01 03:05:44.538 elif tag == 'delete':
2025-07-01 03:05:44.542 atags += '-' * la
2025-07-01 03:05:44.546 elif tag == 'insert':
2025-07-01 03:05:44.551 btags += '+' * lb
2025-07-01 03:05:44.555 elif tag == 'equal':
2025-07-01 03:05:44.559 atags += ' ' * la
2025-07-01 03:05:44.564 btags += ' ' * lb
2025-07-01 03:05:44.568 else:
2025-07-01 03:05:44.573 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:44.577 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:44.582 else:
2025-07-01 03:05:44.586 # the synch pair is identical
2025-07-01 03:05:44.591 yield '  ' + aelt
2025-07-01 03:05:44.595
2025-07-01 03:05:44.600 # pump out diffs from after the synch point
2025-07-01 03:05:44.605 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:44.609
2025-07-01 03:05:44.613 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:44.618 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:44.622
2025-07-01 03:05:44.627 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:44.632 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:44.637 alo = 481, ahi = 1101
2025-07-01 03:05:44.641 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:44.646 blo = 481, bhi = 1101
2025-07-01 03:05:44.650
2025-07-01 03:05:44.655 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:44.659 g = []
2025-07-01 03:05:44.663 if alo < ahi:
2025-07-01 03:05:44.668 if blo < bhi:
2025-07-01 03:05:44.672 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:44.677 else:
2025-07-01 03:05:44.681 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:44.686 elif blo < bhi:
2025-07-01 03:05:44.690 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:44.695
2025-07-01 03:05:44.699 >       yield from g
2025-07-01 03:05:44.703
2025-07-01 03:05:44.708 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:44.712 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:44.716
2025-07-01 03:05:44.721 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:44.725 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:44.730 alo = 481, ahi = 1101
2025-07-01 03:05:44.734 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:44.739 blo = 481, bhi = 1101
2025-07-01 03:05:44.743
2025-07-01 03:05:44.748 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:44.752 r"""
2025-07-01 03:05:44.757 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:44.761 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:44.766 synch point, and intraline difference marking is done on the
2025-07-01 03:05:44.770 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:44.775
2025-07-01 03:05:44.779 Example:
2025-07-01 03:05:44.783
2025-07-01 03:05:44.787 >>> d = Differ()
2025-07-01 03:05:44.792 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:44.796 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:44.800 >>> print(''.join(results), end="")
2025-07-01 03:05:44.805 - abcDefghiJkl
2025-07-01 03:05:44.813 + abcdefGhijkl
2025-07-01 03:05:44.822 """
2025-07-01 03:05:44.826
2025-07-01 03:05:44.830 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:44.835 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:44.839 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:44.844 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:44.849 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:44.853
2025-07-01 03:05:44.857 # search for the pair that matches best without being identical
2025-07-01 03:05:44.862 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:44.866 # on junk -- unless we have to)
2025-07-01 03:05:44.872 for j in range(blo, bhi):
2025-07-01 03:05:44.878 bj = b[j]
2025-07-01 03:05:44.884 cruncher.set_seq2(bj)
2025-07-01 03:05:44.890 for i in range(alo, ahi):
2025-07-01 03:05:44.895 ai = a[i]
2025-07-01 03:05:44.901 if ai == bj:
2025-07-01 03:05:44.907 if eqi is None:
2025-07-01 03:05:44.913 eqi, eqj = i, j
2025-07-01 03:05:44.919 continue
2025-07-01 03:05:44.925 cruncher.set_seq1(ai)
2025-07-01 03:05:44.931 # computing similarity is expensive, so use the quick
2025-07-01 03:05:44.937 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:44.943 # compares by a factor of 3.
2025-07-01 03:05:44.949 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:44.958 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:44.970 # of the computation is cached by cruncher
2025-07-01 03:05:44.978 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:44.983 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:44.988 cruncher.ratio() > best_ratio:
2025-07-01 03:05:44.994 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:44.999 if best_ratio < cutoff:
2025-07-01 03:05:45.004 # no non-identical "pretty close" pair
2025-07-01 03:05:45.011 if eqi is None:
2025-07-01 03:05:45.016 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:45.022 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:45.026 return
2025-07-01 03:05:45.031 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:45.036 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:45.040 else:
2025-07-01 03:05:45.045 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:45.049 eqi = None
2025-07-01 03:05:45.053
2025-07-01 03:05:45.058 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:45.063 # identical
2025-07-01 03:05:45.067
2025-07-01 03:05:45.072 # pump out diffs from before the synch point
2025-07-01 03:05:45.076 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:45.080
2025-07-01 03:05:45.085 # do intraline marking on the synch pair
2025-07-01 03:05:45.089 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:45.094 if eqi is None:
2025-07-01 03:05:45.098 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:45.102 atags = btags = ""
2025-07-01 03:05:45.107 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:45.111 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:45.116 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:45.120 if tag == 'replace':
2025-07-01 03:05:45.124 atags += '^' * la
2025-07-01 03:05:45.129 btags += '^' * lb
2025-07-01 03:05:45.133 elif tag == 'delete':
2025-07-01 03:05:45.137 atags += '-' * la
2025-07-01 03:05:45.142 elif tag == 'insert':
2025-07-01 03:05:45.146 btags += '+' * lb
2025-07-01 03:05:45.151 elif tag == 'equal':
2025-07-01 03:05:45.155 atags += ' ' * la
2025-07-01 03:05:45.160 btags += ' ' * lb
2025-07-01 03:05:45.164 else:
2025-07-01 03:05:45.169 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:45.173 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:45.178 else:
2025-07-01 03:05:45.182 # the synch pair is identical
2025-07-01 03:05:45.186 yield '  ' + aelt
2025-07-01 03:05:45.190
2025-07-01 03:05:45.195 # pump out diffs from after the synch point
2025-07-01 03:05:45.199 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:45.203
2025-07-01 03:05:45.208 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:45.212 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:45.216
2025-07-01 03:05:45.221 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:45.226 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:45.230 alo = 482, ahi = 1101
2025-07-01 03:05:45.235 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:45.239 blo = 482, bhi = 1101
2025-07-01 03:05:45.243
2025-07-01 03:05:45.249 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:45.254 g = []
2025-07-01 03:05:45.258 if alo < ahi:
2025-07-01 03:05:45.263 if blo < bhi:
2025-07-01 03:05:45.268 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:45.273 else:
2025-07-01 03:05:45.278 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:45.282 elif blo < bhi:
2025-07-01 03:05:45.287 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:45.291
2025-07-01 03:05:45.295 >       yield from g
2025-07-01 03:05:45.300
2025-07-01 03:05:45.304 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:45.308 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:45.313
2025-07-01 03:05:45.317 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:45.322 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:45.326 alo = 482, ahi = 1101
2025-07-01 03:05:45.331 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:45.335 blo = 482, bhi = 1101
2025-07-01 03:05:45.339
2025-07-01 03:05:45.344 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:45.348 r"""
2025-07-01 03:05:45.352 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:45.357 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:45.361 synch point, and intraline difference marking is done on the
2025-07-01 03:05:45.365 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:45.370
2025-07-01 03:05:45.375 Example:
2025-07-01 03:05:45.379
2025-07-01 03:05:45.384 >>> d = Differ()
2025-07-01 03:05:45.389 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:45.393 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:45.398 >>> print(''.join(results), end="")
2025-07-01 03:05:45.403 - abcDefghiJkl
2025-07-01 03:05:45.411 + abcdefGhijkl
2025-07-01 03:05:45.420 """
2025-07-01 03:05:45.424
2025-07-01 03:05:45.429 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:45.434 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:45.439 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:45.443 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:45.448 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:45.452
2025-07-01 03:05:45.457 # search for the pair that matches best without being identical
2025-07-01 03:05:45.462 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:45.468 # on junk -- unless we have to)
2025-07-01 03:05:45.472 for j in range(blo, bhi):
2025-07-01 03:05:45.477 bj = b[j]
2025-07-01 03:05:45.481 cruncher.set_seq2(bj)
2025-07-01 03:05:45.486 for i in range(alo, ahi):
2025-07-01 03:05:45.490 ai = a[i]
2025-07-01 03:05:45.495 if ai == bj:
2025-07-01 03:05:45.499 if eqi is None:
2025-07-01 03:05:45.504 eqi, eqj = i, j
2025-07-01 03:05:45.508 continue
2025-07-01 03:05:45.512 cruncher.set_seq1(ai)
2025-07-01 03:05:45.517 # computing similarity is expensive, so use the quick
2025-07-01 03:05:45.522 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:45.527 # compares by a factor of 3.
2025-07-01 03:05:45.531 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:45.535 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:45.540 # of the computation is cached by cruncher
2025-07-01 03:05:45.544 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:45.549 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:45.553 cruncher.ratio() > best_ratio:
2025-07-01 03:05:45.557 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:45.561 if best_ratio < cutoff:
2025-07-01 03:05:45.566 # no non-identical "pretty close" pair
2025-07-01 03:05:45.570 if eqi is None:
2025-07-01 03:05:45.574 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:45.578 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:45.583 return
2025-07-01 03:05:45.587 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:45.591 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:45.596 else:
2025-07-01 03:05:45.600 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:45.604 eqi = None
2025-07-01 03:05:45.608
2025-07-01 03:05:45.612 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:45.617 # identical
2025-07-01 03:05:45.621
2025-07-01 03:05:45.625 # pump out diffs from before the synch point
2025-07-01 03:05:45.630 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:45.634
2025-07-01 03:05:45.638 # do intraline marking on the synch pair
2025-07-01 03:05:45.643 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:45.647 if eqi is None:
2025-07-01 03:05:45.651 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:45.655 atags = btags = ""
2025-07-01 03:05:45.660 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:45.664 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:45.668 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:45.672 if tag == 'replace':
2025-07-01 03:05:45.677 atags += '^' * la
2025-07-01 03:05:45.682 btags += '^' * lb
2025-07-01 03:05:45.686 elif tag == 'delete':
2025-07-01 03:05:45.691 atags += '-' * la
2025-07-01 03:05:45.695 elif tag == 'insert':
2025-07-01 03:05:45.700 btags += '+' * lb
2025-07-01 03:05:45.704 elif tag == 'equal':
2025-07-01 03:05:45.709 atags += ' ' * la
2025-07-01 03:05:45.713 btags += ' ' * lb
2025-07-01 03:05:45.718 else:
2025-07-01 03:05:45.723 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:45.728 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:45.734 else:
2025-07-01 03:05:45.741 # the synch pair is identical
2025-07-01 03:05:45.747 yield '  ' + aelt
2025-07-01 03:05:45.753
2025-07-01 03:05:45.760 # pump out diffs from after the synch point
2025-07-01 03:05:45.766 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:45.773
2025-07-01 03:05:45.780 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:45.787 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:45.795
2025-07-01 03:05:45.801 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:45.807 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:45.811 alo = 483, ahi = 1101
2025-07-01 03:05:45.816 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:45.820 blo = 483, bhi = 1101
2025-07-01 03:05:45.824
2025-07-01 03:05:45.829 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:45.833 g = []
2025-07-01 03:05:45.837 if alo < ahi:
2025-07-01 03:05:45.842 if blo < bhi:
2025-07-01 03:05:45.846 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:45.850 else:
2025-07-01 03:05:45.854 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:45.859 elif blo < bhi:
2025-07-01 03:05:45.863 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:45.867
2025-07-01 03:05:45.871 >       yield from g
2025-07-01 03:05:45.875
2025-07-01 03:05:45.880 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:45.884 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:45.888
2025-07-01 03:05:45.893 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:45.897 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:45.901 alo = 483, ahi = 1101
2025-07-01 03:05:45.906 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:45.911 blo = 483, bhi = 1101
2025-07-01 03:05:45.915
2025-07-01 03:05:45.920 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:45.924 r"""
2025-07-01 03:05:45.928 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:45.933 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:45.937 synch point, and intraline difference marking is done on the
2025-07-01 03:05:45.942 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:45.946
2025-07-01 03:05:45.950 Example:
2025-07-01 03:05:45.955
2025-07-01 03:05:45.959 >>> d = Differ()
2025-07-01 03:05:45.964 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:45.968 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:45.973 >>> print(''.join(results), end="")
2025-07-01 03:05:45.977 - abcDefghiJkl
2025-07-01 03:05:45.986 + abcdefGhijkl
2025-07-01 03:05:45.994 """
2025-07-01 03:05:45.999
2025-07-01 03:05:46.003 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:46.008 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:46.012 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:46.017 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:46.021 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:46.026
2025-07-01 03:05:46.031 # search for the pair that matches best without being identical
2025-07-01 03:05:46.036 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:46.040 # on junk -- unless we have to)
2025-07-01 03:05:46.044 for j in range(blo, bhi):
2025-07-01 03:05:46.049 bj = b[j]
2025-07-01 03:05:46.053 cruncher.set_seq2(bj)
2025-07-01 03:05:46.058 for i in range(alo, ahi):
2025-07-01 03:05:46.062 ai = a[i]
2025-07-01 03:05:46.066 if ai == bj:
2025-07-01 03:05:46.071 if eqi is None:
2025-07-01 03:05:46.076 eqi, eqj = i, j
2025-07-01 03:05:46.082 continue
2025-07-01 03:05:46.088 cruncher.set_seq1(ai)
2025-07-01 03:05:46.094 # computing similarity is expensive, so use the quick
2025-07-01 03:05:46.099 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:46.104 # compares by a factor of 3.
2025-07-01 03:05:46.108 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:46.113 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:46.117 # of the computation is cached by cruncher
2025-07-01 03:05:46.122 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:46.126 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:46.131 cruncher.ratio() > best_ratio:
2025-07-01 03:05:46.135 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:46.139 if best_ratio < cutoff:
2025-07-01 03:05:46.144 # no non-identical "pretty close" pair
2025-07-01 03:05:46.148 if eqi is None:
2025-07-01 03:05:46.152 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:46.157 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:46.161 return
2025-07-01 03:05:46.166 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:46.170 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:46.174 else:
2025-07-01 03:05:46.179 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:46.183 eqi = None
2025-07-01 03:05:46.187
2025-07-01 03:05:46.192 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:46.196 # identical
2025-07-01 03:05:46.200
2025-07-01 03:05:46.204 # pump out diffs from before the synch point
2025-07-01 03:05:46.209 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:46.213
2025-07-01 03:05:46.217 # do intraline marking on the synch pair
2025-07-01 03:05:46.221 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:46.227 if eqi is None:
2025-07-01 03:05:46.231 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:46.235 atags = btags = ""
2025-07-01 03:05:46.240 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:46.244 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:46.248 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:46.253 if tag == 'replace':
2025-07-01 03:05:46.257 atags += '^' * la
2025-07-01 03:05:46.261 btags += '^' * lb
2025-07-01 03:05:46.265 elif tag == 'delete':
2025-07-01 03:05:46.270 atags += '-' * la
2025-07-01 03:05:46.275 elif tag == 'insert':
2025-07-01 03:05:46.280 btags += '+' * lb
2025-07-01 03:05:46.285 elif tag == 'equal':
2025-07-01 03:05:46.289 atags += ' ' * la
2025-07-01 03:05:46.294 btags += ' ' * lb
2025-07-01 03:05:46.298 else:
2025-07-01 03:05:46.303 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:46.307 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:46.311 else:
2025-07-01 03:05:46.316 # the synch pair is identical
2025-07-01 03:05:46.320 yield '  ' + aelt
2025-07-01 03:05:46.324
2025-07-01 03:05:46.329 # pump out diffs from after the synch point
2025-07-01 03:05:46.334 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:46.338
2025-07-01 03:05:46.343 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:46.347 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:46.352
2025-07-01 03:05:46.356 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:46.361 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:46.366 alo = 484, ahi = 1101
2025-07-01 03:05:46.371 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:46.376 blo = 484, bhi = 1101
2025-07-01 03:05:46.381
2025-07-01 03:05:46.386 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:46.390 g = []
2025-07-01 03:05:46.395 if alo < ahi:
2025-07-01 03:05:46.399 if blo < bhi:
2025-07-01 03:05:46.404 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:46.409 else:
2025-07-01 03:05:46.414 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:46.418 elif blo < bhi:
2025-07-01 03:05:46.423 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:46.427
2025-07-01 03:05:46.431 >       yield from g
2025-07-01 03:05:46.436
2025-07-01 03:05:46.441 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:46.446 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:46.451
2025-07-01 03:05:46.457 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:46.463 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:46.469 alo = 484, ahi = 1101
2025-07-01 03:05:46.475 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:46.479 blo = 484, bhi = 1101
2025-07-01 03:05:46.484
2025-07-01 03:05:46.488 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:46.493 r"""
2025-07-01 03:05:46.497 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:46.502 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:46.506 synch point, and intraline difference marking is done on the
2025-07-01 03:05:46.511 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:46.515
2025-07-01 03:05:46.520 Example:
2025-07-01 03:05:46.524
2025-07-01 03:05:46.528 >>> d = Differ()
2025-07-01 03:05:46.533 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:46.538 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:46.542 >>> print(''.join(results), end="")
2025-07-01 03:05:46.547 - abcDefghiJkl
2025-07-01 03:05:46.556 + abcdefGhijkl
2025-07-01 03:05:46.565 """
2025-07-01 03:05:46.569
2025-07-01 03:05:46.574 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:46.579 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:46.583 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:46.588 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:46.592 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:46.596
2025-07-01 03:05:46.601 # search for the pair that matches best without being identical
2025-07-01 03:05:46.605 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:46.610 # on junk -- unless we have to)
2025-07-01 03:05:46.615 for j in range(blo, bhi):
2025-07-01 03:05:46.619 bj = b[j]
2025-07-01 03:05:46.623 cruncher.set_seq2(bj)
2025-07-01 03:05:46.629 for i in range(alo, ahi):
2025-07-01 03:05:46.634 ai = a[i]
2025-07-01 03:05:46.638 if ai == bj:
2025-07-01 03:05:46.643 if eqi is None:
2025-07-01 03:05:46.648 eqi, eqj = i, j
2025-07-01 03:05:46.653 continue
2025-07-01 03:05:46.657 cruncher.set_seq1(ai)
2025-07-01 03:05:46.662 # computing similarity is expensive, so use the quick
2025-07-01 03:05:46.666 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:46.671 # compares by a factor of 3.
2025-07-01 03:05:46.675 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:46.680 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:46.684 # of the computation is cached by cruncher
2025-07-01 03:05:46.689 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:46.693 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:46.697 cruncher.ratio() > best_ratio:
2025-07-01 03:05:46.702 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:46.706 if best_ratio < cutoff:
2025-07-01 03:05:46.711 # no non-identical "pretty close" pair
2025-07-01 03:05:46.715 if eqi is None:
2025-07-01 03:05:46.720 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:46.724 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:46.728 return
2025-07-01 03:05:46.733 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:46.738 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:46.742 else:
2025-07-01 03:05:46.746 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:46.751 eqi = None
2025-07-01 03:05:46.755
2025-07-01 03:05:46.759 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:46.764 # identical
2025-07-01 03:05:46.768
2025-07-01 03:05:46.773 # pump out diffs from before the synch point
2025-07-01 03:05:46.777 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:46.782
2025-07-01 03:05:46.786 # do intraline marking on the synch pair
2025-07-01 03:05:46.790 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:46.795 if eqi is None:
2025-07-01 03:05:46.799 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:46.803 atags = btags = ""
2025-07-01 03:05:46.808 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:46.812 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:46.817 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:46.821 if tag == 'replace':
2025-07-01 03:05:46.826 atags += '^' * la
2025-07-01 03:05:46.830 btags += '^' * lb
2025-07-01 03:05:46.834 elif tag == 'delete':
2025-07-01 03:05:46.839 atags += '-' * la
2025-07-01 03:05:46.843 elif tag == 'insert':
2025-07-01 03:05:46.847 btags += '+' * lb
2025-07-01 03:05:46.852 elif tag == 'equal':
2025-07-01 03:05:46.857 atags += ' ' * la
2025-07-01 03:05:46.861 btags += ' ' * lb
2025-07-01 03:05:46.866 else:
2025-07-01 03:05:46.870 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:46.875 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:46.879 else:
2025-07-01 03:05:46.884 # the synch pair is identical
2025-07-01 03:05:46.888 yield '  ' + aelt
2025-07-01 03:05:46.893
2025-07-01 03:05:46.897 # pump out diffs from after the synch point
2025-07-01 03:05:46.902 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:46.907
2025-07-01 03:05:46.912 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:46.917 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:46.921
2025-07-01 03:05:46.927 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:46.932 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:46.938 alo = 485, ahi = 1101
2025-07-01 03:05:46.944 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:46.949 blo = 485, bhi = 1101
2025-07-01 03:05:46.954
2025-07-01 03:05:46.958 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:46.963 g = []
2025-07-01 03:05:46.968 if alo < ahi:
2025-07-01 03:05:46.973 if blo < bhi:
2025-07-01 03:05:46.978 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:46.983 else:
2025-07-01 03:05:46.987 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:46.992 elif blo < bhi:
2025-07-01 03:05:46.996 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:47.001
2025-07-01 03:05:47.005 >       yield from g
2025-07-01 03:05:47.010
2025-07-01 03:05:47.014 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:47.019 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:47.024
2025-07-01 03:05:47.030 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:47.034 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:47.039 alo = 485, ahi = 1101
2025-07-01 03:05:47.045 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:47.049 blo = 485, bhi = 1101
2025-07-01 03:05:47.054
2025-07-01 03:05:47.059 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:47.064 r"""
2025-07-01 03:05:47.069 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:47.073 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:47.078 synch point, and intraline difference marking is done on the
2025-07-01 03:05:47.082 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:47.086
2025-07-01 03:05:47.091 Example:
2025-07-01 03:05:47.095
2025-07-01 03:05:47.099 >>> d = Differ()
2025-07-01 03:05:47.104 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:47.108 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:47.113 >>> print(''.join(results), end="")
2025-07-01 03:05:47.117 - abcDefghiJkl
2025-07-01 03:05:47.126 + abcdefGhijkl
2025-07-01 03:05:47.135 """
2025-07-01 03:05:47.139
2025-07-01 03:05:47.144 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:47.148 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:47.153 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:47.158 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:47.162 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:47.167
2025-07-01 03:05:47.171 # search for the pair that matches best without being identical
2025-07-01 03:05:47.175 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:47.180 # on junk -- unless we have to)
2025-07-01 03:05:47.184 for j in range(blo, bhi):
2025-07-01 03:05:47.189 bj = b[j]
2025-07-01 03:05:47.193 cruncher.set_seq2(bj)
2025-07-01 03:05:47.198 for i in range(alo, ahi):
2025-07-01 03:05:47.202 ai = a[i]
2025-07-01 03:05:47.206 if ai == bj:
2025-07-01 03:05:47.210 if eqi is None:
2025-07-01 03:05:47.215 eqi, eqj = i, j
2025-07-01 03:05:47.219 continue
2025-07-01 03:05:47.223 cruncher.set_seq1(ai)
2025-07-01 03:05:47.228 # computing similarity is expensive, so use the quick
2025-07-01 03:05:47.232 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:47.236 # compares by a factor of 3.
2025-07-01 03:05:47.241 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:47.245 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:47.250 # of the computation is cached by cruncher
2025-07-01 03:05:47.254 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:47.258 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:47.263 cruncher.ratio() > best_ratio:
2025-07-01 03:05:47.267 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:47.272 if best_ratio < cutoff:
2025-07-01 03:05:47.277 # no non-identical "pretty close" pair
2025-07-01 03:05:47.282 if eqi is None:
2025-07-01 03:05:47.287 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:47.292 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:47.296 return
2025-07-01 03:05:47.300 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:47.305 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:47.309 else:
2025-07-01 03:05:47.313 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:47.319 eqi = None
2025-07-01 03:05:47.324
2025-07-01 03:05:47.330 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:47.335 # identical
2025-07-01 03:05:47.340
2025-07-01 03:05:47.344 # pump out diffs from before the synch point
2025-07-01 03:05:47.349 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:47.353
2025-07-01 03:05:47.358 # do intraline marking on the synch pair
2025-07-01 03:05:47.362 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:47.367 if eqi is None:
2025-07-01 03:05:47.371 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:47.376 atags = btags = ""
2025-07-01 03:05:47.381 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:47.385 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:47.390 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:47.394 if tag == 'replace':
2025-07-01 03:05:47.400 atags += '^' * la
2025-07-01 03:05:47.405 btags += '^' * lb
2025-07-01 03:05:47.410 elif tag == 'delete':
2025-07-01 03:05:47.415 atags += '-' * la
2025-07-01 03:05:47.420 elif tag == 'insert':
2025-07-01 03:05:47.425 btags += '+' * lb
2025-07-01 03:05:47.429 elif tag == 'equal':
2025-07-01 03:05:47.433 atags += ' ' * la
2025-07-01 03:05:47.439 btags += ' ' * lb
2025-07-01 03:05:47.443 else:
2025-07-01 03:05:47.448 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:47.452 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:47.456 else:
2025-07-01 03:05:47.461 # the synch pair is identical
2025-07-01 03:05:47.465 yield '  ' + aelt
2025-07-01 03:05:47.470
2025-07-01 03:05:47.474 # pump out diffs from after the synch point
2025-07-01 03:05:47.479 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:47.483
2025-07-01 03:05:47.488 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:47.493 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:47.497
2025-07-01 03:05:47.502 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:47.507 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:47.512 alo = 488, ahi = 1101
2025-07-01 03:05:47.517 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:47.522 blo = 488, bhi = 1101
2025-07-01 03:05:47.526
2025-07-01 03:05:47.531 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:47.536 g = []
2025-07-01 03:05:47.540 if alo < ahi:
2025-07-01 03:05:47.546 if blo < bhi:
2025-07-01 03:05:47.551 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:47.555 else:
2025-07-01 03:05:47.560 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:47.564 elif blo < bhi:
2025-07-01 03:05:47.569 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:47.573
2025-07-01 03:05:47.578 >       yield from g
2025-07-01 03:05:47.582
2025-07-01 03:05:47.587 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:47.592 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:47.596
2025-07-01 03:05:47.600 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:47.605 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:47.609 alo = 488, ahi = 1101
2025-07-01 03:05:47.614 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:47.619 blo = 488, bhi = 1101
2025-07-01 03:05:47.623
2025-07-01 03:05:47.627 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:47.632 r"""
2025-07-01 03:05:47.636 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:47.641 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:47.646 synch point, and intraline difference marking is done on the
2025-07-01 03:05:47.651 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:47.655
2025-07-01 03:05:47.659 Example:
2025-07-01 03:05:47.663
2025-07-01 03:05:47.668 >>> d = Differ()
2025-07-01 03:05:47.673 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:47.678 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:47.682 >>> print(''.join(results), end="")
2025-07-01 03:05:47.687 - abcDefghiJkl
2025-07-01 03:05:47.697 + abcdefGhijkl
2025-07-01 03:05:47.706 """
2025-07-01 03:05:47.710
2025-07-01 03:05:47.714 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:47.719 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:47.723 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:47.727 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:47.732 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:47.737
2025-07-01 03:05:47.741 # search for the pair that matches best without being identical
2025-07-01 03:05:47.746 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:47.750 # on junk -- unless we have to)
2025-07-01 03:05:47.755 for j in range(blo, bhi):
2025-07-01 03:05:47.759 bj = b[j]
2025-07-01 03:05:47.764 cruncher.set_seq2(bj)
2025-07-01 03:05:47.768 for i in range(alo, ahi):
2025-07-01 03:05:47.773 ai = a[i]
2025-07-01 03:05:47.777 if ai == bj:
2025-07-01 03:05:47.782 if eqi is None:
2025-07-01 03:05:47.786 eqi, eqj = i, j
2025-07-01 03:05:47.790 continue
2025-07-01 03:05:47.795 cruncher.set_seq1(ai)
2025-07-01 03:05:47.799 # computing similarity is expensive, so use the quick
2025-07-01 03:05:47.804 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:47.809 # compares by a factor of 3.
2025-07-01 03:05:47.813 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:47.818 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:47.823 # of the computation is cached by cruncher
2025-07-01 03:05:47.828 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:47.832 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:47.837 cruncher.ratio() > best_ratio:
2025-07-01 03:05:47.842 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:47.846 if best_ratio < cutoff:
2025-07-01 03:05:47.851 # no non-identical "pretty close" pair
2025-07-01 03:05:47.855 if eqi is None:
2025-07-01 03:05:47.860 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:47.864 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:47.868 return
2025-07-01 03:05:47.873 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:47.878 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:47.882 else:
2025-07-01 03:05:47.886 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:47.891 eqi = None
2025-07-01 03:05:47.895
2025-07-01 03:05:47.900 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:47.904 # identical
2025-07-01 03:05:47.908
2025-07-01 03:05:47.912 # pump out diffs from before the synch point
2025-07-01 03:05:47.917 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:47.921
2025-07-01 03:05:47.925 # do intraline marking on the synch pair
2025-07-01 03:05:47.930 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:47.934 if eqi is None:
2025-07-01 03:05:47.939 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:47.943 atags = btags = ""
2025-07-01 03:05:47.948 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:47.953 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:47.958 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:47.962 if tag == 'replace':
2025-07-01 03:05:47.966 atags += '^' * la
2025-07-01 03:05:47.970 btags += '^' * lb
2025-07-01 03:05:47.975 elif tag == 'delete':
2025-07-01 03:05:47.979 atags += '-' * la
2025-07-01 03:05:47.983 elif tag == 'insert':
2025-07-01 03:05:47.988 btags += '+' * lb
2025-07-01 03:05:47.992 elif tag == 'equal':
2025-07-01 03:05:47.996 atags += ' ' * la
2025-07-01 03:05:48.001 btags += ' ' * lb
2025-07-01 03:05:48.005 else:
2025-07-01 03:05:48.010 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:48.014 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:48.018 else:
2025-07-01 03:05:48.023 # the synch pair is identical
2025-07-01 03:05:48.027 yield '  ' + aelt
2025-07-01 03:05:48.031
2025-07-01 03:05:48.036 # pump out diffs from after the synch point
2025-07-01 03:05:48.040 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:48.045
2025-07-01 03:05:48.049 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:48.054 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:48.058
2025-07-01 03:05:48.062 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:48.066 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:48.071 alo = 489, ahi = 1101
2025-07-01 03:05:48.076 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:48.080 blo = 489, bhi = 1101
2025-07-01 03:05:48.084
2025-07-01 03:05:48.089 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:48.093 g = []
2025-07-01 03:05:48.097 if alo < ahi:
2025-07-01 03:05:48.102 if blo < bhi:
2025-07-01 03:05:48.106 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:48.111 else:
2025-07-01 03:05:48.115 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:48.120 elif blo < bhi:
2025-07-01 03:05:48.124 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:48.129
2025-07-01 03:05:48.133 >       yield from g
2025-07-01 03:05:48.137
2025-07-01 03:05:48.142 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:48.146 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:48.151
2025-07-01 03:05:48.155 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:48.160 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:48.164 alo = 489, ahi = 1101
2025-07-01 03:05:48.169 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:48.174 blo = 489, bhi = 1101
2025-07-01 03:05:48.178
2025-07-01 03:05:48.182 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:48.187 r"""
2025-07-01 03:05:48.191 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:48.195 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:48.200 synch point, and intraline difference marking is done on the
2025-07-01 03:05:48.204 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:48.208
2025-07-01 03:05:48.212 Example:
2025-07-01 03:05:48.217
2025-07-01 03:05:48.221 >>> d = Differ()
2025-07-01 03:05:48.225 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:48.230 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:48.234 >>> print(''.join(results), end="")
2025-07-01 03:05:48.238 - abcDefghiJkl
2025-07-01 03:05:48.247 + abcdefGhijkl
2025-07-01 03:05:48.255 """
2025-07-01 03:05:48.260
2025-07-01 03:05:48.264 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:48.269 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:48.274 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:48.278 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:48.282 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:48.286
2025-07-01 03:05:48.291 # search for the pair that matches best without being identical
2025-07-01 03:05:48.295 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:48.300 # on junk -- unless we have to)
2025-07-01 03:05:48.304 for j in range(blo, bhi):
2025-07-01 03:05:48.309 bj = b[j]
2025-07-01 03:05:48.313 cruncher.set_seq2(bj)
2025-07-01 03:05:48.318 for i in range(alo, ahi):
2025-07-01 03:05:48.322 ai = a[i]
2025-07-01 03:05:48.327 if ai == bj:
2025-07-01 03:05:48.331 if eqi is None:
2025-07-01 03:05:48.335 eqi, eqj = i, j
2025-07-01 03:05:48.340 continue
2025-07-01 03:05:48.344 cruncher.set_seq1(ai)
2025-07-01 03:05:48.349 # computing similarity is expensive, so use the quick
2025-07-01 03:05:48.355 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:48.359 # compares by a factor of 3.
2025-07-01 03:05:48.364 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:48.369 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:48.373 # of the computation is cached by cruncher
2025-07-01 03:05:48.378 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:48.383 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:48.388 cruncher.ratio() > best_ratio:
2025-07-01 03:05:48.393 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:48.397 if best_ratio < cutoff:
2025-07-01 03:05:48.402 # no non-identical "pretty close" pair
2025-07-01 03:05:48.406 if eqi is None:
2025-07-01 03:05:48.411 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:48.416 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:48.420 return
2025-07-01 03:05:48.425 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:48.429 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:48.433 else:
2025-07-01 03:05:48.438 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:48.442 eqi = None
2025-07-01 03:05:48.447
2025-07-01 03:05:48.452 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:48.456 # identical
2025-07-01 03:05:48.461
2025-07-01 03:05:48.465 # pump out diffs from before the synch point
2025-07-01 03:05:48.469 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:48.474
2025-07-01 03:05:48.478 # do intraline marking on the synch pair
2025-07-01 03:05:48.483 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:48.488 if eqi is None:
2025-07-01 03:05:48.493 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:48.497 atags = btags = ""
2025-07-01 03:05:48.502 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:48.506 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:48.511 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:48.515 if tag == 'replace':
2025-07-01 03:05:48.520 atags += '^' * la
2025-07-01 03:05:48.524 btags += '^' * lb
2025-07-01 03:05:48.529 elif tag == 'delete':
2025-07-01 03:05:48.533 atags += '-' * la
2025-07-01 03:05:48.537 elif tag == 'insert':
2025-07-01 03:05:48.542 btags += '+' * lb
2025-07-01 03:05:48.547 elif tag == 'equal':
2025-07-01 03:05:48.551 atags += ' ' * la
2025-07-01 03:05:48.555 btags += ' ' * lb
2025-07-01 03:05:48.560 else:
2025-07-01 03:05:48.564 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:48.569 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:48.573 else:
2025-07-01 03:05:48.578 # the synch pair is identical
2025-07-01 03:05:48.582 yield '  ' + aelt
2025-07-01 03:05:48.587
2025-07-01 03:05:48.591 # pump out diffs from after the synch point
2025-07-01 03:05:48.596 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:48.601
2025-07-01 03:05:48.606 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:48.610 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:48.614
2025-07-01 03:05:48.620 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:48.624 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:48.629 alo = 490, ahi = 1101
2025-07-01 03:05:48.634 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:48.638 blo = 490, bhi = 1101
2025-07-01 03:05:48.643
2025-07-01 03:05:48.647 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:48.652 g = []
2025-07-01 03:05:48.657 if alo < ahi:
2025-07-01 03:05:48.661 if blo < bhi:
2025-07-01 03:05:48.666 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:48.670 else:
2025-07-01 03:05:48.682 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:48.698 elif blo < bhi:
2025-07-01 03:05:48.709 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:48.722
2025-07-01 03:05:48.736 >       yield from g
2025-07-01 03:05:48.741
2025-07-01 03:05:48.746 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:48.752 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:48.757
2025-07-01 03:05:48.762 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:48.767 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:48.771 alo = 490, ahi = 1101
2025-07-01 03:05:48.777 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:48.783 blo = 490, bhi = 1101
2025-07-01 03:05:48.789
2025-07-01 03:05:48.796 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:48.802 r"""
2025-07-01 03:05:48.809 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:48.816 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:48.822 synch point, and intraline difference marking is done on the
2025-07-01 03:05:48.827 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:48.832
2025-07-01 03:05:48.837 Example:
2025-07-01 03:05:48.841
2025-07-01 03:05:48.845 >>> d = Differ()
2025-07-01 03:05:48.850 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:48.855 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:48.859 >>> print(''.join(results), end="")
2025-07-01 03:05:48.864 - abcDefghiJkl
2025-07-01 03:05:48.872 + abcdefGhijkl
2025-07-01 03:05:48.881 """
2025-07-01 03:05:48.885
2025-07-01 03:05:48.891 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:48.896 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:48.900 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:48.904 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:48.909 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:48.914
2025-07-01 03:05:48.918 # search for the pair that matches best without being identical
2025-07-01 03:05:48.923 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:48.927 # on junk -- unless we have to)
2025-07-01 03:05:48.931 for j in range(blo, bhi):
2025-07-01 03:05:48.936 bj = b[j]
2025-07-01 03:05:48.940 cruncher.set_seq2(bj)
2025-07-01 03:05:48.945 for i in range(alo, ahi):
2025-07-01 03:05:48.949 ai = a[i]
2025-07-01 03:05:48.953 if ai == bj:
2025-07-01 03:05:48.957 if eqi is None:
2025-07-01 03:05:48.962 eqi, eqj = i, j
2025-07-01 03:05:48.966 continue
2025-07-01 03:05:48.970 cruncher.set_seq1(ai)
2025-07-01 03:05:48.975 # computing similarity is expensive, so use the quick
2025-07-01 03:05:48.979 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:48.983 # compares by a factor of 3.
2025-07-01 03:05:48.988 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:48.992 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:48.997 # of the computation is cached by cruncher
2025-07-01 03:05:49.001 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:49.006 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:49.010 cruncher.ratio() > best_ratio:
2025-07-01 03:05:49.014 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:49.019 if best_ratio < cutoff:
2025-07-01 03:05:49.023 # no non-identical "pretty close" pair
2025-07-01 03:05:49.027 if eqi is None:
2025-07-01 03:05:49.032 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:49.036 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:49.041 return
2025-07-01 03:05:49.045 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:49.050 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:49.054 else:
2025-07-01 03:05:49.059 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:49.063 eqi = None
2025-07-01 03:05:49.068
2025-07-01 03:05:49.072 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:49.076 # identical
2025-07-01 03:05:49.081
2025-07-01 03:05:49.085 # pump out diffs from before the synch point
2025-07-01 03:05:49.089 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:49.094
2025-07-01 03:05:49.099 # do intraline marking on the synch pair
2025-07-01 03:05:49.103 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:49.108 if eqi is None:
2025-07-01 03:05:49.112 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:49.117 atags = btags = ""
2025-07-01 03:05:49.121 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:49.125 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:49.130 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:49.134 if tag == 'replace':
2025-07-01 03:05:49.139 atags += '^' * la
2025-07-01 03:05:49.143 btags += '^' * lb
2025-07-01 03:05:49.147 elif tag == 'delete':
2025-07-01 03:05:49.152 atags += '-' * la
2025-07-01 03:05:49.156 elif tag == 'insert':
2025-07-01 03:05:49.161 btags += '+' * lb
2025-07-01 03:05:49.165 elif tag == 'equal':
2025-07-01 03:05:49.170 atags += ' ' * la
2025-07-01 03:05:49.174 btags += ' ' * lb
2025-07-01 03:05:49.178 else:
2025-07-01 03:05:49.183 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:49.187 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:49.192 else:
2025-07-01 03:05:49.196 # the synch pair is identical
2025-07-01 03:05:49.201 yield '  ' + aelt
2025-07-01 03:05:49.206
2025-07-01 03:05:49.211 # pump out diffs from after the synch point
2025-07-01 03:05:49.215 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:49.219
2025-07-01 03:05:49.224 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:49.228 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:49.232
2025-07-01 03:05:49.237 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:49.241 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:49.246 alo = 491, ahi = 1101
2025-07-01 03:05:49.251 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:49.255 blo = 491, bhi = 1101
2025-07-01 03:05:49.259
2025-07-01 03:05:49.263 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:49.268 g = []
2025-07-01 03:05:49.272 if alo < ahi:
2025-07-01 03:05:49.276 if blo < bhi:
2025-07-01 03:05:49.281 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:49.285 else:
2025-07-01 03:05:49.289 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:49.294 elif blo < bhi:
2025-07-01 03:05:49.299 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:49.305
2025-07-01 03:05:49.309 >       yield from g
2025-07-01 03:05:49.313
2025-07-01 03:05:49.318 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:49.323 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:49.327
2025-07-01 03:05:49.332 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:49.336 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:49.341 alo = 491, ahi = 1101
2025-07-01 03:05:49.345 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:49.350 blo = 491, bhi = 1101
2025-07-01 03:05:49.354
2025-07-01 03:05:49.361 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:49.366 r"""
2025-07-01 03:05:49.371 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:49.375 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:49.380 synch point, and intraline difference marking is done on the
2025-07-01 03:05:49.385 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:49.390
2025-07-01 03:05:49.395 Example:
2025-07-01 03:05:49.401
2025-07-01 03:05:49.406 >>> d = Differ()
2025-07-01 03:05:49.410 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:49.415 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:49.419 >>> print(''.join(results), end="")
2025-07-01 03:05:49.424 - abcDefghiJkl
2025-07-01 03:05:49.434 + abcdefGhijkl
2025-07-01 03:05:49.443 """
2025-07-01 03:05:49.447
2025-07-01 03:05:49.452 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:49.458 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:49.463 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:49.467 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:49.473 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:49.477
2025-07-01 03:05:49.482 # search for the pair that matches best without being identical
2025-07-01 03:05:49.486 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:49.491 # on junk -- unless we have to)
2025-07-01 03:05:49.495 for j in range(blo, bhi):
2025-07-01 03:05:49.500 bj = b[j]
2025-07-01 03:05:49.504 cruncher.set_seq2(bj)
2025-07-01 03:05:49.510 for i in range(alo, ahi):
2025-07-01 03:05:49.515 ai = a[i]
2025-07-01 03:05:49.519 if ai == bj:
2025-07-01 03:05:49.524 if eqi is None:
2025-07-01 03:05:49.529 eqi, eqj = i, j
2025-07-01 03:05:49.533 continue
2025-07-01 03:05:49.538 cruncher.set_seq1(ai)
2025-07-01 03:05:49.543 # computing similarity is expensive, so use the quick
2025-07-01 03:05:49.548 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:49.552 # compares by a factor of 3.
2025-07-01 03:05:49.557 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:49.562 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:49.567 # of the computation is cached by cruncher
2025-07-01 03:05:49.571 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:49.576 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:49.582 cruncher.ratio() > best_ratio:
2025-07-01 03:05:49.586 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:49.591 if best_ratio < cutoff:
2025-07-01 03:05:49.595 # no non-identical "pretty close" pair
2025-07-01 03:05:49.600 if eqi is None:
2025-07-01 03:05:49.605 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:49.609 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:49.614 return
2025-07-01 03:05:49.618 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:49.623 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:49.627 else:
2025-07-01 03:05:49.632 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:49.636 eqi = None
2025-07-01 03:05:49.640
2025-07-01 03:05:49.645 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:49.652 # identical
2025-07-01 03:05:49.656
2025-07-01 03:05:49.661 # pump out diffs from before the synch point
2025-07-01 03:05:49.666 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:49.671
2025-07-01 03:05:49.676 # do intraline marking on the synch pair
2025-07-01 03:05:49.680 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:49.685 if eqi is None:
2025-07-01 03:05:49.690 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:49.694 atags = btags = ""
2025-07-01 03:05:49.699 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:49.704 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:49.708 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:49.713 if tag == 'replace':
2025-07-01 03:05:49.717 atags += '^' * la
2025-07-01 03:05:49.722 btags += '^' * lb
2025-07-01 03:05:49.726 elif tag == 'delete':
2025-07-01 03:05:49.731 atags += '-' * la
2025-07-01 03:05:49.735 elif tag == 'insert':
2025-07-01 03:05:49.740 btags += '+' * lb
2025-07-01 03:05:49.744 elif tag == 'equal':
2025-07-01 03:05:49.748 atags += ' ' * la
2025-07-01 03:05:49.753 btags += ' ' * lb
2025-07-01 03:05:49.757 else:
2025-07-01 03:05:49.762 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:49.766 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:49.770 else:
2025-07-01 03:05:49.775 # the synch pair is identical
2025-07-01 03:05:49.779 yield '  ' + aelt
2025-07-01 03:05:49.784
2025-07-01 03:05:49.789 # pump out diffs from after the synch point
2025-07-01 03:05:49.794 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:49.798
2025-07-01 03:05:49.803 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:49.807 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:49.811
2025-07-01 03:05:49.816 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:49.821 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:49.825 alo = 492, ahi = 1101
2025-07-01 03:05:49.830 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:49.835 blo = 492, bhi = 1101
2025-07-01 03:05:49.839
2025-07-01 03:05:49.844 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:49.848 g = []
2025-07-01 03:05:49.853 if alo < ahi:
2025-07-01 03:05:49.857 if blo < bhi:
2025-07-01 03:05:49.862 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:49.866 else:
2025-07-01 03:05:49.871 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:49.875 elif blo < bhi:
2025-07-01 03:05:49.880 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:49.884
2025-07-01 03:05:49.888 >       yield from g
2025-07-01 03:05:49.893
2025-07-01 03:05:49.897 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:49.902 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:49.906
2025-07-01 03:05:49.911 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:49.916 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:49.920 alo = 492, ahi = 1101
2025-07-01 03:05:49.925 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:49.930 blo = 492, bhi = 1101
2025-07-01 03:05:49.934
2025-07-01 03:05:49.938 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:49.943 r"""
2025-07-01 03:05:49.947 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:49.951 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:49.956 synch point, and intraline difference marking is done on the
2025-07-01 03:05:49.961 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:49.965
2025-07-01 03:05:49.970 Example:
2025-07-01 03:05:49.974
2025-07-01 03:05:49.979 >>> d = Differ()
2025-07-01 03:05:49.984 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:49.988 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:49.992 >>> print(''.join(results), end="")
2025-07-01 03:05:49.997 - abcDefghiJkl
2025-07-01 03:05:50.007 + abcdefGhijkl
2025-07-01 03:05:50.016 """
2025-07-01 03:05:50.021
2025-07-01 03:05:50.026 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:50.030 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:50.035 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:50.039 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:50.044 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:50.050
2025-07-01 03:05:50.055 # search for the pair that matches best without being identical
2025-07-01 03:05:50.061 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:50.067 # on junk -- unless we have to)
2025-07-01 03:05:50.073 for j in range(blo, bhi):
2025-07-01 03:05:50.078 bj = b[j]
2025-07-01 03:05:50.084 cruncher.set_seq2(bj)
2025-07-01 03:05:50.090 for i in range(alo, ahi):
2025-07-01 03:05:50.096 ai = a[i]
2025-07-01 03:05:50.101 if ai == bj:
2025-07-01 03:05:50.107 if eqi is None:
2025-07-01 03:05:50.113 eqi, eqj = i, j
2025-07-01 03:05:50.119 continue
2025-07-01 03:05:50.124 cruncher.set_seq1(ai)
2025-07-01 03:05:50.129 # computing similarity is expensive, so use the quick
2025-07-01 03:05:50.133 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:50.138 # compares by a factor of 3.
2025-07-01 03:05:50.143 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:50.148 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:50.154 # of the computation is cached by cruncher
2025-07-01 03:05:50.159 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:50.164 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:50.169 cruncher.ratio() > best_ratio:
2025-07-01 03:05:50.175 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:50.180 if best_ratio < cutoff:
2025-07-01 03:05:50.184 # no non-identical "pretty close" pair
2025-07-01 03:05:50.189 if eqi is None:
2025-07-01 03:05:50.193 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:50.198 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:50.203 return
2025-07-01 03:05:50.208 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:50.212 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:50.217 else:
2025-07-01 03:05:50.221 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:50.226 eqi = None
2025-07-01 03:05:50.230
2025-07-01 03:05:50.235 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:50.239 # identical
2025-07-01 03:05:50.243
2025-07-01 03:05:50.248 # pump out diffs from before the synch point
2025-07-01 03:05:50.252 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:50.256
2025-07-01 03:05:50.261 # do intraline marking on the synch pair
2025-07-01 03:05:50.265 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:50.269 if eqi is None:
2025-07-01 03:05:50.274 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:50.278 atags = btags = ""
2025-07-01 03:05:50.283 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:50.288 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:50.293 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:50.297 if tag == 'replace':
2025-07-01 03:05:50.301 atags += '^' * la
2025-07-01 03:05:50.306 btags += '^' * lb
2025-07-01 03:05:50.310 elif tag == 'delete':
2025-07-01 03:05:50.315 atags += '-' * la
2025-07-01 03:05:50.319 elif tag == 'insert':
2025-07-01 03:05:50.323 btags += '+' * lb
2025-07-01 03:05:50.328 elif tag == 'equal':
2025-07-01 03:05:50.332 atags += ' ' * la
2025-07-01 03:05:50.337 btags += ' ' * lb
2025-07-01 03:05:50.341 else:
2025-07-01 03:05:50.345 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:50.350 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:50.354 else:
2025-07-01 03:05:50.358 # the synch pair is identical
2025-07-01 03:05:50.363 yield '  ' + aelt
2025-07-01 03:05:50.368
2025-07-01 03:05:50.372 # pump out diffs from after the synch point
2025-07-01 03:05:50.377 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:50.381
2025-07-01 03:05:50.385 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:50.390 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:50.394
2025-07-01 03:05:50.399 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:50.405 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:50.409 alo = 493, ahi = 1101
2025-07-01 03:05:50.414 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:50.418 blo = 493, bhi = 1101
2025-07-01 03:05:50.423
2025-07-01 03:05:50.427 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:50.432 g = []
2025-07-01 03:05:50.436 if alo < ahi:
2025-07-01 03:05:50.441 if blo < bhi:
2025-07-01 03:05:50.447 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:50.452 else:
2025-07-01 03:05:50.458 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:50.464 elif blo < bhi:
2025-07-01 03:05:50.469 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:50.474
2025-07-01 03:05:50.478 >       yield from g
2025-07-01 03:05:50.482
2025-07-01 03:05:50.486 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:50.491 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:50.495
2025-07-01 03:05:50.499 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:50.504 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:50.508 alo = 493, ahi = 1101
2025-07-01 03:05:50.513 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:50.517 blo = 493, bhi = 1101
2025-07-01 03:05:50.521
2025-07-01 03:05:50.526 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:50.530 r"""
2025-07-01 03:05:50.534 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:50.539 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:50.543 synch point, and intraline difference marking is done on the
2025-07-01 03:05:50.548 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:50.552
2025-07-01 03:05:50.556 Example:
2025-07-01 03:05:50.560
2025-07-01 03:05:50.565 >>> d = Differ()
2025-07-01 03:05:50.569 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:50.574 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:50.578 >>> print(''.join(results), end="")
2025-07-01 03:05:50.582 - abcDefghiJkl
2025-07-01 03:05:50.591 + abcdefGhijkl
2025-07-01 03:05:50.599 """
2025-07-01 03:05:50.603
2025-07-01 03:05:50.608 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:50.612 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:50.617 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:50.621 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:50.625 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:50.630
2025-07-01 03:05:50.634 # search for the pair that matches best without being identical
2025-07-01 03:05:50.638 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:50.643 # on junk -- unless we have to)
2025-07-01 03:05:50.647 for j in range(blo, bhi):
2025-07-01 03:05:50.651 bj = b[j]
2025-07-01 03:05:50.655 cruncher.set_seq2(bj)
2025-07-01 03:05:50.660 for i in range(alo, ahi):
2025-07-01 03:05:50.664 ai = a[i]
2025-07-01 03:05:50.668 if ai == bj:
2025-07-01 03:05:50.672 if eqi is None:
2025-07-01 03:05:50.677 eqi, eqj = i, j
2025-07-01 03:05:50.681 continue
2025-07-01 03:05:50.685 cruncher.set_seq1(ai)
2025-07-01 03:05:50.690 # computing similarity is expensive, so use the quick
2025-07-01 03:05:50.694 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:50.699 # compares by a factor of 3.
2025-07-01 03:05:50.703 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:50.708 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:50.712 # of the computation is cached by cruncher
2025-07-01 03:05:50.716 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:50.721 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:50.725 cruncher.ratio() > best_ratio:
2025-07-01 03:05:50.730 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:50.734 if best_ratio < cutoff:
2025-07-01 03:05:50.739 # no non-identical "pretty close" pair
2025-07-01 03:05:50.744 if eqi is None:
2025-07-01 03:05:50.748 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:50.753 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:50.757 return
2025-07-01 03:05:50.761 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:50.766 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:50.770 else:
2025-07-01 03:05:50.775 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:50.779 eqi = None
2025-07-01 03:05:50.784
2025-07-01 03:05:50.788 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:50.792 # identical
2025-07-01 03:05:50.797
2025-07-01 03:05:50.801 # pump out diffs from before the synch point
2025-07-01 03:05:50.805 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:50.810
2025-07-01 03:05:50.814 # do intraline marking on the synch pair
2025-07-01 03:05:50.819 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:50.823 if eqi is None:
2025-07-01 03:05:50.828 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:50.832 atags = btags = ""
2025-07-01 03:05:50.837 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:50.841 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:50.846 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:50.850 if tag == 'replace':
2025-07-01 03:05:50.855 atags += '^' * la
2025-07-01 03:05:50.859 btags += '^' * lb
2025-07-01 03:05:50.864 elif tag == 'delete':
2025-07-01 03:05:50.868 atags += '-' * la
2025-07-01 03:05:50.873 elif tag == 'insert':
2025-07-01 03:05:50.877 btags += '+' * lb
2025-07-01 03:05:50.882 elif tag == 'equal':
2025-07-01 03:05:50.886 atags += ' ' * la
2025-07-01 03:05:50.891 btags += ' ' * lb
2025-07-01 03:05:50.895 else:
2025-07-01 03:05:50.899 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:50.904 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:50.908 else:
2025-07-01 03:05:50.913 # the synch pair is identical
2025-07-01 03:05:50.917 yield '  ' + aelt
2025-07-01 03:05:50.922
2025-07-01 03:05:50.926 # pump out diffs from after the synch point
2025-07-01 03:05:50.931 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:50.935
2025-07-01 03:05:50.939 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:50.944 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:50.949
2025-07-01 03:05:50.953 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:50.958 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:50.962 alo = 494, ahi = 1101
2025-07-01 03:05:50.967 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:50.971 blo = 494, bhi = 1101
2025-07-01 03:05:50.976
2025-07-01 03:05:50.980 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:50.985 g = []
2025-07-01 03:05:50.989 if alo < ahi:
2025-07-01 03:05:50.993 if blo < bhi:
2025-07-01 03:05:50.998 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:51.002 else:
2025-07-01 03:05:51.007 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:51.011 elif blo < bhi:
2025-07-01 03:05:51.016 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:51.020
2025-07-01 03:05:51.026 >       yield from g
2025-07-01 03:05:51.030
2025-07-01 03:05:51.035 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:51.040 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:51.044
2025-07-01 03:05:51.049 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:51.053 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:51.059 alo = 494, ahi = 1101
2025-07-01 03:05:51.063 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:51.068 blo = 494, bhi = 1101
2025-07-01 03:05:51.072
2025-07-01 03:05:51.076 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:51.081 r"""
2025-07-01 03:05:51.085 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:51.090 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:51.094 synch point, and intraline difference marking is done on the
2025-07-01 03:05:51.099 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:51.104
2025-07-01 03:05:51.108 Example:
2025-07-01 03:05:51.113
2025-07-01 03:05:51.117 >>> d = Differ()
2025-07-01 03:05:51.122 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:51.127 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:51.132 >>> print(''.join(results), end="")
2025-07-01 03:05:51.136 - abcDefghiJkl
2025-07-01 03:05:51.145 + abcdefGhijkl
2025-07-01 03:05:51.154 """
2025-07-01 03:05:51.159
2025-07-01 03:05:51.163 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:51.168 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:51.173 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:51.178 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:51.183 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:51.187
2025-07-01 03:05:51.192 # search for the pair that matches best without being identical
2025-07-01 03:05:51.197 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:51.201 # on junk -- unless we have to)
2025-07-01 03:05:51.206 for j in range(blo, bhi):
2025-07-01 03:05:51.210 bj = b[j]
2025-07-01 03:05:51.215 cruncher.set_seq2(bj)
2025-07-01 03:05:51.220 for i in range(alo, ahi):
2025-07-01 03:05:51.225 ai = a[i]
2025-07-01 03:05:51.229 if ai == bj:
2025-07-01 03:05:51.235 if eqi is None:
2025-07-01 03:05:51.239 eqi, eqj = i, j
2025-07-01 03:05:51.244 continue
2025-07-01 03:05:51.248 cruncher.set_seq1(ai)
2025-07-01 03:05:51.253 # computing similarity is expensive, so use the quick
2025-07-01 03:05:51.258 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:51.263 # compares by a factor of 3.
2025-07-01 03:05:51.267 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:51.272 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:51.277 # of the computation is cached by cruncher
2025-07-01 03:05:51.281 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:51.286 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:51.291 cruncher.ratio() > best_ratio:
2025-07-01 03:05:51.295 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:51.300 if best_ratio < cutoff:
2025-07-01 03:05:51.305 # no non-identical "pretty close" pair
2025-07-01 03:05:51.309 if eqi is None:
2025-07-01 03:05:51.314 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:51.318 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:51.323 return
2025-07-01 03:05:51.328 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:51.333 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:51.338 else:
2025-07-01 03:05:51.343 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:51.347 eqi = None
2025-07-01 03:05:51.351
2025-07-01 03:05:51.356 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:51.360 # identical
2025-07-01 03:05:51.365
2025-07-01 03:05:51.370 # pump out diffs from before the synch point
2025-07-01 03:05:51.376 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:51.380
2025-07-01 03:05:51.385 # do intraline marking on the synch pair
2025-07-01 03:05:51.389 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:51.394 if eqi is None:
2025-07-01 03:05:51.399 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:51.405 atags = btags = ""
2025-07-01 03:05:51.410 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:51.415 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:51.420 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:51.425 if tag == 'replace':
2025-07-01 03:05:51.430 atags += '^' * la
2025-07-01 03:05:51.435 btags += '^' * lb
2025-07-01 03:05:51.440 elif tag == 'delete':
2025-07-01 03:05:51.444 atags += '-' * la
2025-07-01 03:05:51.449 elif tag == 'insert':
2025-07-01 03:05:51.454 btags += '+' * lb
2025-07-01 03:05:51.458 elif tag == 'equal':
2025-07-01 03:05:51.463 atags += ' ' * la
2025-07-01 03:05:51.467 btags += ' ' * lb
2025-07-01 03:05:51.472 else:
2025-07-01 03:05:51.477 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:51.482 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:51.486 else:
2025-07-01 03:05:51.491 # the synch pair is identical
2025-07-01 03:05:51.495 yield '  ' + aelt
2025-07-01 03:05:51.500
2025-07-01 03:05:51.504 # pump out diffs from after the synch point
2025-07-01 03:05:51.510 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:51.515
2025-07-01 03:05:51.519 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:51.524 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:51.528
2025-07-01 03:05:51.533 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:51.537 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:51.542 alo = 495, ahi = 1101
2025-07-01 03:05:51.546 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:51.551 blo = 495, bhi = 1101
2025-07-01 03:05:51.555
2025-07-01 03:05:51.560 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:51.564 g = []
2025-07-01 03:05:51.569 if alo < ahi:
2025-07-01 03:05:51.573 if blo < bhi:
2025-07-01 03:05:51.577 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:51.582 else:
2025-07-01 03:05:51.586 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:51.591 elif blo < bhi:
2025-07-01 03:05:51.595 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:51.600
2025-07-01 03:05:51.604 >       yield from g
2025-07-01 03:05:51.608
2025-07-01 03:05:51.613 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:51.617 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:51.621
2025-07-01 03:05:51.626 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:51.640 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:51.651 alo = 495, ahi = 1101
2025-07-01 03:05:51.660 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:51.665 blo = 495, bhi = 1101
2025-07-01 03:05:51.670
2025-07-01 03:05:51.675 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:51.680 r"""
2025-07-01 03:05:51.685 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:51.689 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:51.694 synch point, and intraline difference marking is done on the
2025-07-01 03:05:51.699 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:51.703
2025-07-01 03:05:51.707 Example:
2025-07-01 03:05:51.712
2025-07-01 03:05:51.716 >>> d = Differ()
2025-07-01 03:05:51.721 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:51.725 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:51.730 >>> print(''.join(results), end="")
2025-07-01 03:05:51.735 - abcDefghiJkl
2025-07-01 03:05:51.750 + abcdefGhijkl
2025-07-01 03:05:51.769 """
2025-07-01 03:05:51.776
2025-07-01 03:05:51.783 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:51.789 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:51.794 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:51.800 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:51.805 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:51.810
2025-07-01 03:05:51.815 # search for the pair that matches best without being identical
2025-07-01 03:05:51.819 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:51.824 # on junk -- unless we have to)
2025-07-01 03:05:51.828 for j in range(blo, bhi):
2025-07-01 03:05:51.833 bj = b[j]
2025-07-01 03:05:51.837 cruncher.set_seq2(bj)
2025-07-01 03:05:51.842 for i in range(alo, ahi):
2025-07-01 03:05:51.846 ai = a[i]
2025-07-01 03:05:51.851 if ai == bj:
2025-07-01 03:05:51.855 if eqi is None:
2025-07-01 03:05:51.860 eqi, eqj = i, j
2025-07-01 03:05:51.864 continue
2025-07-01 03:05:51.868 cruncher.set_seq1(ai)
2025-07-01 03:05:51.873 # computing similarity is expensive, so use the quick
2025-07-01 03:05:51.877 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:51.883 # compares by a factor of 3.
2025-07-01 03:05:51.887 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:51.892 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:51.897 # of the computation is cached by cruncher
2025-07-01 03:05:51.902 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:51.907 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:51.911 cruncher.ratio() > best_ratio:
2025-07-01 03:05:51.918 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:51.923 if best_ratio < cutoff:
2025-07-01 03:05:51.928 # no non-identical "pretty close" pair
2025-07-01 03:05:51.932 if eqi is None:
2025-07-01 03:05:51.938 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:51.942 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:51.947 return
2025-07-01 03:05:51.952 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:51.957 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:51.962 else:
2025-07-01 03:05:51.966 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:51.970 eqi = None
2025-07-01 03:05:51.975
2025-07-01 03:05:51.980 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:51.985 # identical
2025-07-01 03:05:51.989
2025-07-01 03:05:51.994 # pump out diffs from before the synch point
2025-07-01 03:05:51.999 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:52.004
2025-07-01 03:05:52.009 # do intraline marking on the synch pair
2025-07-01 03:05:52.013 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:52.018 if eqi is None:
2025-07-01 03:05:52.023 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:52.027 atags = btags = ""
2025-07-01 03:05:52.032 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:52.037 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:52.042 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:52.046 if tag == 'replace':
2025-07-01 03:05:52.051 atags += '^' * la
2025-07-01 03:05:52.055 btags += '^' * lb
2025-07-01 03:05:52.060 elif tag == 'delete':
2025-07-01 03:05:52.064 atags += '-' * la
2025-07-01 03:05:52.069 elif tag == 'insert':
2025-07-01 03:05:52.073 btags += '+' * lb
2025-07-01 03:05:52.077 elif tag == 'equal':
2025-07-01 03:05:52.082 atags += ' ' * la
2025-07-01 03:05:52.086 btags += ' ' * lb
2025-07-01 03:05:52.090 else:
2025-07-01 03:05:52.095 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:52.099 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:52.104 else:
2025-07-01 03:05:52.108 # the synch pair is identical
2025-07-01 03:05:52.113 yield '  ' + aelt
2025-07-01 03:05:52.117
2025-07-01 03:05:52.122 # pump out diffs from after the synch point
2025-07-01 03:05:52.126 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:52.131
2025-07-01 03:05:52.136 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:52.140 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:52.145
2025-07-01 03:05:52.149 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:52.154 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:52.158 alo = 496, ahi = 1101
2025-07-01 03:05:52.164 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:52.169 blo = 496, bhi = 1101
2025-07-01 03:05:52.173
2025-07-01 03:05:52.178 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:52.183 g = []
2025-07-01 03:05:52.187 if alo < ahi:
2025-07-01 03:05:52.192 if blo < bhi:
2025-07-01 03:05:52.197 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:52.201 else:
2025-07-01 03:05:52.206 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:52.210 elif blo < bhi:
2025-07-01 03:05:52.214 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:52.219
2025-07-01 03:05:52.223 >       yield from g
2025-07-01 03:05:52.227
2025-07-01 03:05:52.232 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:52.236 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:52.240
2025-07-01 03:05:52.245 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:52.249 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:52.253 alo = 496, ahi = 1101
2025-07-01 03:05:52.258 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:52.262 blo = 496, bhi = 1101
2025-07-01 03:05:52.267
2025-07-01 03:05:52.271 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:52.275 r"""
2025-07-01 03:05:52.280 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:52.284 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:52.289 synch point, and intraline difference marking is done on the
2025-07-01 03:05:52.293 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:52.298
2025-07-01 03:05:52.302 Example:
2025-07-01 03:05:52.307
2025-07-01 03:05:52.312 >>> d = Differ()
2025-07-01 03:05:52.316 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:52.320 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:52.325 >>> print(''.join(results), end="")
2025-07-01 03:05:52.329 - abcDefghiJkl
2025-07-01 03:05:52.338 + abcdefGhijkl
2025-07-01 03:05:52.347 """
2025-07-01 03:05:52.351
2025-07-01 03:05:52.355 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:52.360 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:52.365 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:52.370 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:52.374 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:52.379
2025-07-01 03:05:52.384 # search for the pair that matches best without being identical
2025-07-01 03:05:52.389 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:52.393 # on junk -- unless we have to)
2025-07-01 03:05:52.398 for j in range(blo, bhi):
2025-07-01 03:05:52.402 bj = b[j]
2025-07-01 03:05:52.407 cruncher.set_seq2(bj)
2025-07-01 03:05:52.411 for i in range(alo, ahi):
2025-07-01 03:05:52.416 ai = a[i]
2025-07-01 03:05:52.420 if ai == bj:
2025-07-01 03:05:52.424 if eqi is None:
2025-07-01 03:05:52.429 eqi, eqj = i, j
2025-07-01 03:05:52.434 continue
2025-07-01 03:05:52.438 cruncher.set_seq1(ai)
2025-07-01 03:05:52.444 # computing similarity is expensive, so use the quick
2025-07-01 03:05:52.449 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:52.453 # compares by a factor of 3.
2025-07-01 03:05:52.458 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:52.462 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:52.467 # of the computation is cached by cruncher
2025-07-01 03:05:52.471 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:52.476 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:52.480 cruncher.ratio() > best_ratio:
2025-07-01 03:05:52.484 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:52.488 if best_ratio < cutoff:
2025-07-01 03:05:52.493 # no non-identical "pretty close" pair
2025-07-01 03:05:52.497 if eqi is None:
2025-07-01 03:05:52.502 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:52.506 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:52.510 return
2025-07-01 03:05:52.515 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:52.519 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:52.524 else:
2025-07-01 03:05:52.528 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:52.532 eqi = None
2025-07-01 03:05:52.537
2025-07-01 03:05:52.541 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:52.545 # identical
2025-07-01 03:05:52.550
2025-07-01 03:05:52.554 # pump out diffs from before the synch point
2025-07-01 03:05:52.558 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:52.563
2025-07-01 03:05:52.567 # do intraline marking on the synch pair
2025-07-01 03:05:52.571 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:52.576 if eqi is None:
2025-07-01 03:05:52.580 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:52.585 atags = btags = ""
2025-07-01 03:05:52.589 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:52.593 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:52.598 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:52.602 if tag == 'replace':
2025-07-01 03:05:52.606 atags += '^' * la
2025-07-01 03:05:52.610 btags += '^' * lb
2025-07-01 03:05:52.615 elif tag == 'delete':
2025-07-01 03:05:52.619 atags += '-' * la
2025-07-01 03:05:52.623 elif tag == 'insert':
2025-07-01 03:05:52.627 btags += '+' * lb
2025-07-01 03:05:52.632 elif tag == 'equal':
2025-07-01 03:05:52.636 atags += ' ' * la
2025-07-01 03:05:52.640 btags += ' ' * lb
2025-07-01 03:05:52.645 else:
2025-07-01 03:05:52.650 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:52.654 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:52.658 else:
2025-07-01 03:05:52.663 # the synch pair is identical
2025-07-01 03:05:52.667 yield '  ' + aelt
2025-07-01 03:05:52.671
2025-07-01 03:05:52.675 # pump out diffs from after the synch point
2025-07-01 03:05:52.680 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:52.684
2025-07-01 03:05:52.688 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:52.693 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:52.697
2025-07-01 03:05:52.701 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:52.706 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:52.711 alo = 497, ahi = 1101
2025-07-01 03:05:52.715 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:52.719 blo = 497, bhi = 1101
2025-07-01 03:05:52.723
2025-07-01 03:05:52.727 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:52.732 g = []
2025-07-01 03:05:52.736 if alo < ahi:
2025-07-01 03:05:52.741 if blo < bhi:
2025-07-01 03:05:52.745 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:52.750 else:
2025-07-01 03:05:52.754 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:52.758 elif blo < bhi:
2025-07-01 03:05:52.763 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:52.767
2025-07-01 03:05:52.771 >       yield from g
2025-07-01 03:05:52.775
2025-07-01 03:05:52.779 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:52.784 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:52.789
2025-07-01 03:05:52.793 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:52.797 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:52.802 alo = 497, ahi = 1101
2025-07-01 03:05:52.806 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:52.811 blo = 497, bhi = 1101
2025-07-01 03:05:52.815
2025-07-01 03:05:52.819 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:52.823 r"""
2025-07-01 03:05:52.828 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:52.832 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:52.836 synch point, and intraline difference marking is done on the
2025-07-01 03:05:52.841 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:52.845
2025-07-01 03:05:52.849 Example:
2025-07-01 03:05:52.853
2025-07-01 03:05:52.858 >>> d = Differ()
2025-07-01 03:05:52.862 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:52.866 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:52.871 >>> print(''.join(results), end="")
2025-07-01 03:05:52.875 - abcDefghiJkl
2025-07-01 03:05:52.883 + abcdefGhijkl
2025-07-01 03:05:52.892 """
2025-07-01 03:05:52.896
2025-07-01 03:05:52.900 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:52.905 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:52.909 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:52.913 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:52.918 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:52.922
2025-07-01 03:05:52.926 # search for the pair that matches best without being identical
2025-07-01 03:05:52.931 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:52.935 # on junk -- unless we have to)
2025-07-01 03:05:52.939 for j in range(blo, bhi):
2025-07-01 03:05:52.943 bj = b[j]
2025-07-01 03:05:52.948 cruncher.set_seq2(bj)
2025-07-01 03:05:52.952 for i in range(alo, ahi):
2025-07-01 03:05:52.957 ai = a[i]
2025-07-01 03:05:52.961 if ai == bj:
2025-07-01 03:05:52.966 if eqi is None:
2025-07-01 03:05:52.970 eqi, eqj = i, j
2025-07-01 03:05:52.974 continue
2025-07-01 03:05:52.979 cruncher.set_seq1(ai)
2025-07-01 03:05:52.983 # computing similarity is expensive, so use the quick
2025-07-01 03:05:52.988 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:52.992 # compares by a factor of 3.
2025-07-01 03:05:52.997 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:53.002 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:53.006 # of the computation is cached by cruncher
2025-07-01 03:05:53.011 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:53.015 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:53.020 cruncher.ratio() > best_ratio:
2025-07-01 03:05:53.024 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:53.029 if best_ratio < cutoff:
2025-07-01 03:05:53.034 # no non-identical "pretty close" pair
2025-07-01 03:05:53.038 if eqi is None:
2025-07-01 03:05:53.043 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:53.047 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:53.052 return
2025-07-01 03:05:53.056 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:53.061 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:53.065 else:
2025-07-01 03:05:53.070 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:53.074 eqi = None
2025-07-01 03:05:53.078
2025-07-01 03:05:53.083 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:53.087 # identical
2025-07-01 03:05:53.091
2025-07-01 03:05:53.096 # pump out diffs from before the synch point
2025-07-01 03:05:53.100 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:53.104
2025-07-01 03:05:53.109 # do intraline marking on the synch pair
2025-07-01 03:05:53.113 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:53.118 if eqi is None:
2025-07-01 03:05:53.122 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:53.126 atags = btags = ""
2025-07-01 03:05:53.131 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:53.135 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:53.140 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:53.144 if tag == 'replace':
2025-07-01 03:05:53.149 atags += '^' * la
2025-07-01 03:05:53.153 btags += '^' * lb
2025-07-01 03:05:53.158 elif tag == 'delete':
2025-07-01 03:05:53.163 atags += '-' * la
2025-07-01 03:05:53.168 elif tag == 'insert':
2025-07-01 03:05:53.173 btags += '+' * lb
2025-07-01 03:05:53.177 elif tag == 'equal':
2025-07-01 03:05:53.182 atags += ' ' * la
2025-07-01 03:05:53.186 btags += ' ' * lb
2025-07-01 03:05:53.190 else:
2025-07-01 03:05:53.195 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:53.199 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:53.204 else:
2025-07-01 03:05:53.208 # the synch pair is identical
2025-07-01 03:05:53.212 yield '  ' + aelt
2025-07-01 03:05:53.217
2025-07-01 03:05:53.221 # pump out diffs from after the synch point
2025-07-01 03:05:53.225 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:53.229
2025-07-01 03:05:53.234 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:53.238 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:53.242
2025-07-01 03:05:53.247 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:53.251 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:53.255 alo = 498, ahi = 1101
2025-07-01 03:05:53.260 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:53.264 blo = 498, bhi = 1101
2025-07-01 03:05:53.268
2025-07-01 03:05:53.273 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:53.277 g = []
2025-07-01 03:05:53.281 if alo < ahi:
2025-07-01 03:05:53.286 if blo < bhi:
2025-07-01 03:05:53.290 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:53.294 else:
2025-07-01 03:05:53.299 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:53.303 elif blo < bhi:
2025-07-01 03:05:53.307 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:53.312
2025-07-01 03:05:53.316 >       yield from g
2025-07-01 03:05:53.320
2025-07-01 03:05:53.324 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:53.329 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:53.333
2025-07-01 03:05:53.337 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:53.342 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:53.346 alo = 498, ahi = 1101
2025-07-01 03:05:53.351 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:53.356 blo = 498, bhi = 1101
2025-07-01 03:05:53.360
2025-07-01 03:05:53.365 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:53.369 r"""
2025-07-01 03:05:53.374 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:53.378 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:53.383 synch point, and intraline difference marking is done on the
2025-07-01 03:05:53.387 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:53.392
2025-07-01 03:05:53.396 Example:
2025-07-01 03:05:53.400
2025-07-01 03:05:53.405 >>> d = Differ()
2025-07-01 03:05:53.410 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:53.414 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:53.418 >>> print(''.join(results), end="")
2025-07-01 03:05:53.423 - abcDefghiJkl
2025-07-01 03:05:53.431 + abcdefGhijkl
2025-07-01 03:05:53.440 """
2025-07-01 03:05:53.444
2025-07-01 03:05:53.449 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:53.453 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:53.457 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:53.462 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:53.466 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:53.471
2025-07-01 03:05:53.478 # search for the pair that matches best without being identical
2025-07-01 03:05:53.483 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:53.487 # on junk -- unless we have to)
2025-07-01 03:05:53.491 for j in range(blo, bhi):
2025-07-01 03:05:53.495 bj = b[j]
2025-07-01 03:05:53.500 cruncher.set_seq2(bj)
2025-07-01 03:05:53.504 for i in range(alo, ahi):
2025-07-01 03:05:53.508 ai = a[i]
2025-07-01 03:05:53.513 if ai == bj:
2025-07-01 03:05:53.517 if eqi is None:
2025-07-01 03:05:53.521 eqi, eqj = i, j
2025-07-01 03:05:53.526 continue
2025-07-01 03:05:53.530 cruncher.set_seq1(ai)
2025-07-01 03:05:53.534 # computing similarity is expensive, so use the quick
2025-07-01 03:05:53.539 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:53.543 # compares by a factor of 3.
2025-07-01 03:05:53.547 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:53.552 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:53.556 # of the computation is cached by cruncher
2025-07-01 03:05:53.560 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:53.565 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:53.569 cruncher.ratio() > best_ratio:
2025-07-01 03:05:53.573 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:53.578 if best_ratio < cutoff:
2025-07-01 03:05:53.582 # no non-identical "pretty close" pair
2025-07-01 03:05:53.586 if eqi is None:
2025-07-01 03:05:53.591 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:53.595 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:53.599 return
2025-07-01 03:05:53.604 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:53.608 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:53.612 else:
2025-07-01 03:05:53.617 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:53.621 eqi = None
2025-07-01 03:05:53.625
2025-07-01 03:05:53.630 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:53.634 # identical
2025-07-01 03:05:53.638
2025-07-01 03:05:53.643 # pump out diffs from before the synch point
2025-07-01 03:05:53.647 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:53.651
2025-07-01 03:05:53.656 # do intraline marking on the synch pair
2025-07-01 03:05:53.660 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:53.665 if eqi is None:
2025-07-01 03:05:53.669 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:53.674 atags = btags = ""
2025-07-01 03:05:53.678 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:53.683 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:53.687 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:53.691 if tag == 'replace':
2025-07-01 03:05:53.696 atags += '^' * la
2025-07-01 03:05:53.700 btags += '^' * lb
2025-07-01 03:05:53.704 elif tag == 'delete':
2025-07-01 03:05:53.709 atags += '-' * la
2025-07-01 03:05:53.713 elif tag == 'insert':
2025-07-01 03:05:53.717 btags += '+' * lb
2025-07-01 03:05:53.722 elif tag == 'equal':
2025-07-01 03:05:53.726 atags += ' ' * la
2025-07-01 03:05:53.730 btags += ' ' * lb
2025-07-01 03:05:53.735 else:
2025-07-01 03:05:53.739 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:53.743 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:53.747 else:
2025-07-01 03:05:53.752 # the synch pair is identical
2025-07-01 03:05:53.756 yield '  ' + aelt
2025-07-01 03:05:53.760
2025-07-01 03:05:53.765 # pump out diffs from after the synch point
2025-07-01 03:05:53.769 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:53.773
2025-07-01 03:05:53.777 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:53.782 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:53.786
2025-07-01 03:05:53.790 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:53.795 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:53.799 alo = 499, ahi = 1101
2025-07-01 03:05:53.804 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:53.808 blo = 499, bhi = 1101
2025-07-01 03:05:53.812
2025-07-01 03:05:53.817 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:53.821 g = []
2025-07-01 03:05:53.825 if alo < ahi:
2025-07-01 03:05:53.830 if blo < bhi:
2025-07-01 03:05:53.834 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:53.838 else:
2025-07-01 03:05:53.843 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:53.847 elif blo < bhi:
2025-07-01 03:05:53.851 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:53.855
2025-07-01 03:05:53.860 >       yield from g
2025-07-01 03:05:53.865
2025-07-01 03:05:53.869 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:53.873 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:53.878
2025-07-01 03:05:53.882 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:53.887 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:53.891 alo = 499, ahi = 1101
2025-07-01 03:05:53.896 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:53.901 blo = 499, bhi = 1101
2025-07-01 03:05:53.905
2025-07-01 03:05:53.909 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:53.914 r"""
2025-07-01 03:05:53.918 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:53.923 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:53.927 synch point, and intraline difference marking is done on the
2025-07-01 03:05:53.932 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:53.937
2025-07-01 03:05:53.941 Example:
2025-07-01 03:05:53.946
2025-07-01 03:05:53.950 >>> d = Differ()
2025-07-01 03:05:53.955 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:53.959 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:53.964 >>> print(''.join(results), end="")
2025-07-01 03:05:53.968 - abcDefghiJkl
2025-07-01 03:05:53.977 + abcdefGhijkl
2025-07-01 03:05:53.986 """
2025-07-01 03:05:53.991
2025-07-01 03:05:53.996 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:54.000 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:54.005 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:54.010 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:54.014 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:54.018
2025-07-01 03:05:54.023 # search for the pair that matches best without being identical
2025-07-01 03:05:54.027 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:54.032 # on junk -- unless we have to)
2025-07-01 03:05:54.036 for j in range(blo, bhi):
2025-07-01 03:05:54.040 bj = b[j]
2025-07-01 03:05:54.045 cruncher.set_seq2(bj)
2025-07-01 03:05:54.049 for i in range(alo, ahi):
2025-07-01 03:05:54.053 ai = a[i]
2025-07-01 03:05:54.058 if ai == bj:
2025-07-01 03:05:54.062 if eqi is None:
2025-07-01 03:05:54.067 eqi, eqj = i, j
2025-07-01 03:05:54.071 continue
2025-07-01 03:05:54.076 cruncher.set_seq1(ai)
2025-07-01 03:05:54.080 # computing similarity is expensive, so use the quick
2025-07-01 03:05:54.085 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:54.089 # compares by a factor of 3.
2025-07-01 03:05:54.094 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:54.099 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:54.103 # of the computation is cached by cruncher
2025-07-01 03:05:54.108 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:54.112 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:54.116 cruncher.ratio() > best_ratio:
2025-07-01 03:05:54.121 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:54.126 if best_ratio < cutoff:
2025-07-01 03:05:54.130 # no non-identical "pretty close" pair
2025-07-01 03:05:54.134 if eqi is None:
2025-07-01 03:05:54.139 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:54.143 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:54.148 return
2025-07-01 03:05:54.152 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:54.157 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:54.161 else:
2025-07-01 03:05:54.166 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:54.170 eqi = None
2025-07-01 03:05:54.175
2025-07-01 03:05:54.179 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:54.184 # identical
2025-07-01 03:05:54.188
2025-07-01 03:05:54.192 # pump out diffs from before the synch point
2025-07-01 03:05:54.197 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:54.202
2025-07-01 03:05:54.207 # do intraline marking on the synch pair
2025-07-01 03:05:54.211 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:54.216 if eqi is None:
2025-07-01 03:05:54.221 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:54.226 atags = btags = ""
2025-07-01 03:05:54.231 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:54.235 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:54.240 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:54.244 if tag == 'replace':
2025-07-01 03:05:54.249 atags += '^' * la
2025-07-01 03:05:54.253 btags += '^' * lb
2025-07-01 03:05:54.257 elif tag == 'delete':
2025-07-01 03:05:54.262 atags += '-' * la
2025-07-01 03:05:54.267 elif tag == 'insert':
2025-07-01 03:05:54.271 btags += '+' * lb
2025-07-01 03:05:54.275 elif tag == 'equal':
2025-07-01 03:05:54.280 atags += ' ' * la
2025-07-01 03:05:54.284 btags += ' ' * lb
2025-07-01 03:05:54.289 else:
2025-07-01 03:05:54.293 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:54.298 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:54.302 else:
2025-07-01 03:05:54.307 # the synch pair is identical
2025-07-01 03:05:54.311 yield '  ' + aelt
2025-07-01 03:05:54.316
2025-07-01 03:05:54.320 # pump out diffs from after the synch point
2025-07-01 03:05:54.325 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:54.329
2025-07-01 03:05:54.333 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:54.338 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:54.343
2025-07-01 03:05:54.348 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:54.353 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:54.358 alo = 500, ahi = 1101
2025-07-01 03:05:54.362 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:54.367 blo = 500, bhi = 1101
2025-07-01 03:05:54.371
2025-07-01 03:05:54.375 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:54.380 g = []
2025-07-01 03:05:54.384 if alo < ahi:
2025-07-01 03:05:54.389 if blo < bhi:
2025-07-01 03:05:54.394 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:54.398 else:
2025-07-01 03:05:54.403 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:54.407 elif blo < bhi:
2025-07-01 03:05:54.411 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:54.416
2025-07-01 03:05:54.420 >       yield from g
2025-07-01 03:05:54.424
2025-07-01 03:05:54.428 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:54.433 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:54.437
2025-07-01 03:05:54.441 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:54.446 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:54.450 alo = 500, ahi = 1101
2025-07-01 03:05:54.455 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:54.459 blo = 500, bhi = 1101
2025-07-01 03:05:54.463
2025-07-01 03:05:54.468 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:54.472 r"""
2025-07-01 03:05:54.476 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:54.481 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:54.485 synch point, and intraline difference marking is done on the
2025-07-01 03:05:54.489 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:54.493
2025-07-01 03:05:54.497 Example:
2025-07-01 03:05:54.503
2025-07-01 03:05:54.508 >>> d = Differ()
2025-07-01 03:05:54.514 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:54.518 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:54.523 >>> print(''.join(results), end="")
2025-07-01 03:05:54.527 - abcDefghiJkl
2025-07-01 03:05:54.537 + abcdefGhijkl
2025-07-01 03:05:54.545 """
2025-07-01 03:05:54.550
2025-07-01 03:05:54.554 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:54.558 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:54.563 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:54.567 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:54.571 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:54.575
2025-07-01 03:05:54.580 # search for the pair that matches best without being identical
2025-07-01 03:05:54.584 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:54.590 # on junk -- unless we have to)
2025-07-01 03:05:54.596 for j in range(blo, bhi):
2025-07-01 03:05:54.602 bj = b[j]
2025-07-01 03:05:54.609 cruncher.set_seq2(bj)
2025-07-01 03:05:54.615 for i in range(alo, ahi):
2025-07-01 03:05:54.621 ai = a[i]
2025-07-01 03:05:54.627 if ai == bj:
2025-07-01 03:05:54.633 if eqi is None:
2025-07-01 03:05:54.638 eqi, eqj = i, j
2025-07-01 03:05:54.643 continue
2025-07-01 03:05:54.647 cruncher.set_seq1(ai)
2025-07-01 03:05:54.652 # computing similarity is expensive, so use the quick
2025-07-01 03:05:54.656 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:54.661 # compares by a factor of 3.
2025-07-01 03:05:54.665 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:54.669 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:54.673 # of the computation is cached by cruncher
2025-07-01 03:05:54.678 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:54.683 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:54.687 cruncher.ratio() > best_ratio:
2025-07-01 03:05:54.692 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:54.696 if best_ratio < cutoff:
2025-07-01 03:05:54.700 # no non-identical "pretty close" pair
2025-07-01 03:05:54.705 if eqi is None:
2025-07-01 03:05:54.709 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:54.713 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:54.718 return
2025-07-01 03:05:54.722 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:54.727 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:54.731 else:
2025-07-01 03:05:54.735 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:54.740 eqi = None
2025-07-01 03:05:54.744
2025-07-01 03:05:54.748 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:54.752 # identical
2025-07-01 03:05:54.757
2025-07-01 03:05:54.761 # pump out diffs from before the synch point
2025-07-01 03:05:54.767 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:54.773
2025-07-01 03:05:54.778 # do intraline marking on the synch pair
2025-07-01 03:05:54.783 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:54.787 if eqi is None:
2025-07-01 03:05:54.792 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:54.797 atags = btags = ""
2025-07-01 03:05:54.801 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:54.806 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:54.810 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:54.814 if tag == 'replace':
2025-07-01 03:05:54.818 atags += '^' * la
2025-07-01 03:05:54.823 btags += '^' * lb
2025-07-01 03:05:54.827 elif tag == 'delete':
2025-07-01 03:05:54.832 atags += '-' * la
2025-07-01 03:05:54.836 elif tag == 'insert':
2025-07-01 03:05:54.840 btags += '+' * lb
2025-07-01 03:05:54.845 elif tag == 'equal':
2025-07-01 03:05:54.849 atags += ' ' * la
2025-07-01 03:05:54.853 btags += ' ' * lb
2025-07-01 03:05:54.858 else:
2025-07-01 03:05:54.862 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:54.866 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:54.870 else:
2025-07-01 03:05:54.875 # the synch pair is identical
2025-07-01 03:05:54.879 yield '  ' + aelt
2025-07-01 03:05:54.883
2025-07-01 03:05:54.888 # pump out diffs from after the synch point
2025-07-01 03:05:54.892 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:54.896
2025-07-01 03:05:54.900 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:54.905 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:54.909
2025-07-01 03:05:54.913 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:54.918 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:54.922 alo = 501, ahi = 1101
2025-07-01 03:05:54.927 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:54.931 blo = 501, bhi = 1101
2025-07-01 03:05:54.936
2025-07-01 03:05:54.940 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:54.945 g = []
2025-07-01 03:05:54.949 if alo < ahi:
2025-07-01 03:05:54.953 if blo < bhi:
2025-07-01 03:05:54.958 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:54.962 else:
2025-07-01 03:05:54.966 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:54.971 elif blo < bhi:
2025-07-01 03:05:54.975 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:54.979
2025-07-01 03:05:54.984 >       yield from g
2025-07-01 03:05:54.988
2025-07-01 03:05:54.992 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:54.997 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:55.001
2025-07-01 03:05:55.006 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:55.010 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:55.015 alo = 501, ahi = 1101
2025-07-01 03:05:55.021 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:55.027 blo = 501, bhi = 1101
2025-07-01 03:05:55.032
2025-07-01 03:05:55.036 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:55.042 r"""
2025-07-01 03:05:55.046 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:55.051 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:55.055 synch point, and intraline difference marking is done on the
2025-07-01 03:05:55.059 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:55.064
2025-07-01 03:05:55.068 Example:
2025-07-01 03:05:55.072
2025-07-01 03:05:55.077 >>> d = Differ()
2025-07-01 03:05:55.081 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:55.086 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:55.090 >>> print(''.join(results), end="")
2025-07-01 03:05:55.094 - abcDefghiJkl
2025-07-01 03:05:55.103 + abcdefGhijkl
2025-07-01 03:05:55.111 """
2025-07-01 03:05:55.116
2025-07-01 03:05:55.120 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:55.125 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:55.129 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:55.133 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:55.138 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:55.142
2025-07-01 03:05:55.146 # search for the pair that matches best without being identical
2025-07-01 03:05:55.151 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:55.155 # on junk -- unless we have to)
2025-07-01 03:05:55.160 for j in range(blo, bhi):
2025-07-01 03:05:55.164 bj = b[j]
2025-07-01 03:05:55.168 cruncher.set_seq2(bj)
2025-07-01 03:05:55.173 for i in range(alo, ahi):
2025-07-01 03:05:55.177 ai = a[i]
2025-07-01 03:05:55.181 if ai == bj:
2025-07-01 03:05:55.186 if eqi is None:
2025-07-01 03:05:55.190 eqi, eqj = i, j
2025-07-01 03:05:55.194 continue
2025-07-01 03:05:55.199 cruncher.set_seq1(ai)
2025-07-01 03:05:55.203 # computing similarity is expensive, so use the quick
2025-07-01 03:05:55.207 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:55.212 # compares by a factor of 3.
2025-07-01 03:05:55.216 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:55.221 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:55.225 # of the computation is cached by cruncher
2025-07-01 03:05:55.229 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:55.234 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:55.238 cruncher.ratio() > best_ratio:
2025-07-01 03:05:55.242 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:55.247 if best_ratio < cutoff:
2025-07-01 03:05:55.251 # no non-identical "pretty close" pair
2025-07-01 03:05:55.255 if eqi is None:
2025-07-01 03:05:55.260 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:55.264 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:55.268 return
2025-07-01 03:05:55.273 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:55.277 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:55.282 else:
2025-07-01 03:05:55.286 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:55.290 eqi = None
2025-07-01 03:05:55.295
2025-07-01 03:05:55.299 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:55.303 # identical
2025-07-01 03:05:55.307
2025-07-01 03:05:55.312 # pump out diffs from before the synch point
2025-07-01 03:05:55.316 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:55.321
2025-07-01 03:05:55.325 # do intraline marking on the synch pair
2025-07-01 03:05:55.329 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:55.333 if eqi is None:
2025-07-01 03:05:55.338 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:55.342 atags = btags = ""
2025-07-01 03:05:55.346 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:55.351 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:55.355 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:55.360 if tag == 'replace':
2025-07-01 03:05:55.364 atags += '^' * la
2025-07-01 03:05:55.368 btags += '^' * lb
2025-07-01 03:05:55.373 elif tag == 'delete':
2025-07-01 03:05:55.377 atags += '-' * la
2025-07-01 03:05:55.381 elif tag == 'insert':
2025-07-01 03:05:55.386 btags += '+' * lb
2025-07-01 03:05:55.390 elif tag == 'equal':
2025-07-01 03:05:55.394 atags += ' ' * la
2025-07-01 03:05:55.398 btags += ' ' * lb
2025-07-01 03:05:55.403 else:
2025-07-01 03:05:55.407 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:55.411 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:55.416 else:
2025-07-01 03:05:55.420 # the synch pair is identical
2025-07-01 03:05:55.424 yield '  ' + aelt
2025-07-01 03:05:55.428
2025-07-01 03:05:55.433 # pump out diffs from after the synch point
2025-07-01 03:05:55.437 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:55.441
2025-07-01 03:05:55.446 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:55.450 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:55.454
2025-07-01 03:05:55.459 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:55.463 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:55.467 alo = 502, ahi = 1101
2025-07-01 03:05:55.472 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:55.477 blo = 502, bhi = 1101
2025-07-01 03:05:55.481
2025-07-01 03:05:55.485 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:55.489 g = []
2025-07-01 03:05:55.494 if alo < ahi:
2025-07-01 03:05:55.498 if blo < bhi:
2025-07-01 03:05:55.502 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:55.506 else:
2025-07-01 03:05:55.511 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:55.515 elif blo < bhi:
2025-07-01 03:05:55.519 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:55.524
2025-07-01 03:05:55.529 >       yield from g
2025-07-01 03:05:55.534
2025-07-01 03:05:55.538 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:55.542 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:55.546
2025-07-01 03:05:55.551 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:55.555 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:55.560 alo = 502, ahi = 1101
2025-07-01 03:05:55.564 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:55.569 blo = 502, bhi = 1101
2025-07-01 03:05:55.573
2025-07-01 03:05:55.577 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:55.582 r"""
2025-07-01 03:05:55.586 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:55.591 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:55.596 synch point, and intraline difference marking is done on the
2025-07-01 03:05:55.600 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:55.604
2025-07-01 03:05:55.609 Example:
2025-07-01 03:05:55.613
2025-07-01 03:05:55.617 >>> d = Differ()
2025-07-01 03:05:55.622 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:55.626 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:55.630 >>> print(''.join(results), end="")
2025-07-01 03:05:55.634 - abcDefghiJkl
2025-07-01 03:05:55.643 + abcdefGhijkl
2025-07-01 03:05:55.652 """
2025-07-01 03:05:55.656
2025-07-01 03:05:55.661 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:55.665 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:55.670 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:55.674 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:55.679 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:55.683
2025-07-01 03:05:55.687 # search for the pair that matches best without being identical
2025-07-01 03:05:55.692 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:55.696 # on junk -- unless we have to)
2025-07-01 03:05:55.701 for j in range(blo, bhi):
2025-07-01 03:05:55.705 bj = b[j]
2025-07-01 03:05:55.709 cruncher.set_seq2(bj)
2025-07-01 03:05:55.713 for i in range(alo, ahi):
2025-07-01 03:05:55.718 ai = a[i]
2025-07-01 03:05:55.722 if ai == bj:
2025-07-01 03:05:55.727 if eqi is None:
2025-07-01 03:05:55.731 eqi, eqj = i, j
2025-07-01 03:05:55.735 continue
2025-07-01 03:05:55.740 cruncher.set_seq1(ai)
2025-07-01 03:05:55.744 # computing similarity is expensive, so use the quick
2025-07-01 03:05:55.749 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:55.754 # compares by a factor of 3.
2025-07-01 03:05:55.758 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:55.762 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:55.767 # of the computation is cached by cruncher
2025-07-01 03:05:55.771 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:55.776 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:55.780 cruncher.ratio() > best_ratio:
2025-07-01 03:05:55.787 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:55.793 if best_ratio < cutoff:
2025-07-01 03:05:55.799 # no non-identical "pretty close" pair
2025-07-01 03:05:55.805 if eqi is None:
2025-07-01 03:05:55.812 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:55.818 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:55.824 return
2025-07-01 03:05:55.832 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:55.838 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:55.844 else:
2025-07-01 03:05:55.850 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:55.856 eqi = None
2025-07-01 03:05:55.862
2025-07-01 03:05:55.868 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:55.873 # identical
2025-07-01 03:05:55.879
2025-07-01 03:05:55.885 # pump out diffs from before the synch point
2025-07-01 03:05:55.891 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:55.897
2025-07-01 03:05:55.903 # do intraline marking on the synch pair
2025-07-01 03:05:55.909 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:55.914 if eqi is None:
2025-07-01 03:05:55.918 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:55.923 atags = btags = ""
2025-07-01 03:05:55.927 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:55.932 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:55.936 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:55.941 if tag == 'replace':
2025-07-01 03:05:55.945 atags += '^' * la
2025-07-01 03:05:55.949 btags += '^' * lb
2025-07-01 03:05:55.953 elif tag == 'delete':
2025-07-01 03:05:55.958 atags += '-' * la
2025-07-01 03:05:55.962 elif tag == 'insert':
2025-07-01 03:05:55.966 btags += '+' * lb
2025-07-01 03:05:55.971 elif tag == 'equal':
2025-07-01 03:05:55.975 atags += ' ' * la
2025-07-01 03:05:55.982 btags += ' ' * lb
2025-07-01 03:05:55.988 else:
2025-07-01 03:05:55.993 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:55.997 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:56.002 else:
2025-07-01 03:05:56.006 # the synch pair is identical
2025-07-01 03:05:56.011 yield '  ' + aelt
2025-07-01 03:05:56.015
2025-07-01 03:05:56.020 # pump out diffs from after the synch point
2025-07-01 03:05:56.024 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:56.028
2025-07-01 03:05:56.033 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:56.038 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:56.042
2025-07-01 03:05:56.046 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:56.051 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:56.055 alo = 503, ahi = 1101
2025-07-01 03:05:56.060 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:56.064 blo = 503, bhi = 1101
2025-07-01 03:05:56.068
2025-07-01 03:05:56.073 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:56.077 g = []
2025-07-01 03:05:56.082 if alo < ahi:
2025-07-01 03:05:56.086 if blo < bhi:
2025-07-01 03:05:56.090 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:56.095 else:
2025-07-01 03:05:56.099 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:56.103 elif blo < bhi:
2025-07-01 03:05:56.108 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:56.112
2025-07-01 03:05:56.116 >       yield from g
2025-07-01 03:05:56.121
2025-07-01 03:05:56.125 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:56.129 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:56.134
2025-07-01 03:05:56.138 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:56.143 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:56.147 alo = 503, ahi = 1101
2025-07-01 03:05:56.152 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:56.157 blo = 503, bhi = 1101
2025-07-01 03:05:56.161
2025-07-01 03:05:56.166 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:56.170 r"""
2025-07-01 03:05:56.175 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:56.179 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:56.184 synch point, and intraline difference marking is done on the
2025-07-01 03:05:56.188 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:56.193
2025-07-01 03:05:56.197 Example:
2025-07-01 03:05:56.201
2025-07-01 03:05:56.205 >>> d = Differ()
2025-07-01 03:05:56.210 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:56.214 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:56.218 >>> print(''.join(results), end="")
2025-07-01 03:05:56.223 - abcDefghiJkl
2025-07-01 03:05:56.231 + abcdefGhijkl
2025-07-01 03:05:56.242 """
2025-07-01 03:05:56.246
2025-07-01 03:05:56.250 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:56.255 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:56.259 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:56.263 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:56.268 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:56.272
2025-07-01 03:05:56.277 # search for the pair that matches best without being identical
2025-07-01 03:05:56.281 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:56.286 # on junk -- unless we have to)
2025-07-01 03:05:56.290 for j in range(blo, bhi):
2025-07-01 03:05:56.294 bj = b[j]
2025-07-01 03:05:56.299 cruncher.set_seq2(bj)
2025-07-01 03:05:56.303 for i in range(alo, ahi):
2025-07-01 03:05:56.307 ai = a[i]
2025-07-01 03:05:56.311 if ai == bj:
2025-07-01 03:05:56.316 if eqi is None:
2025-07-01 03:05:56.320 eqi, eqj = i, j
2025-07-01 03:05:56.325 continue
2025-07-01 03:05:56.329 cruncher.set_seq1(ai)
2025-07-01 03:05:56.334 # computing similarity is expensive, so use the quick
2025-07-01 03:05:56.338 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:56.343 # compares by a factor of 3.
2025-07-01 03:05:56.348 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:56.352 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:56.357 # of the computation is cached by cruncher
2025-07-01 03:05:56.361 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:56.365 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:56.370 cruncher.ratio() > best_ratio:
2025-07-01 03:05:56.374 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:56.379 if best_ratio < cutoff:
2025-07-01 03:05:56.383 # no non-identical "pretty close" pair
2025-07-01 03:05:56.387 if eqi is None:
2025-07-01 03:05:56.392 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:56.396 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:56.400 return
2025-07-01 03:05:56.405 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:56.409 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:56.414 else:
2025-07-01 03:05:56.418 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:56.423 eqi = None
2025-07-01 03:05:56.427
2025-07-01 03:05:56.432 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:56.436 # identical
2025-07-01 03:05:56.440
2025-07-01 03:05:56.445 # pump out diffs from before the synch point
2025-07-01 03:05:56.449 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:56.454
2025-07-01 03:05:56.458 # do intraline marking on the synch pair
2025-07-01 03:05:56.462 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:56.466 if eqi is None:
2025-07-01 03:05:56.471 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:56.475 atags = btags = ""
2025-07-01 03:05:56.479 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:56.484 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:56.488 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:56.492 if tag == 'replace':
2025-07-01 03:05:56.497 atags += '^' * la
2025-07-01 03:05:56.501 btags += '^' * lb
2025-07-01 03:05:56.505 elif tag == 'delete':
2025-07-01 03:05:56.509 atags += '-' * la
2025-07-01 03:05:56.514 elif tag == 'insert':
2025-07-01 03:05:56.518 btags += '+' * lb
2025-07-01 03:05:56.522 elif tag == 'equal':
2025-07-01 03:05:56.527 atags += ' ' * la
2025-07-01 03:05:56.531 btags += ' ' * lb
2025-07-01 03:05:56.535 else:
2025-07-01 03:05:56.540 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:56.544 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:56.549 else:
2025-07-01 03:05:56.554 # the synch pair is identical
2025-07-01 03:05:56.559 yield '  ' + aelt
2025-07-01 03:05:56.564
2025-07-01 03:05:56.569 # pump out diffs from after the synch point
2025-07-01 03:05:56.573 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:56.577
2025-07-01 03:05:56.582 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:56.586 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:56.591
2025-07-01 03:05:56.596 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:56.600 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:56.604 alo = 504, ahi = 1101
2025-07-01 03:05:56.610 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:56.614 blo = 504, bhi = 1101
2025-07-01 03:05:56.618
2025-07-01 03:05:56.623 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:56.628 g = []
2025-07-01 03:05:56.632 if alo < ahi:
2025-07-01 03:05:56.636 if blo < bhi:
2025-07-01 03:05:56.641 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:56.646 else:
2025-07-01 03:05:56.650 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:56.655 elif blo < bhi:
2025-07-01 03:05:56.660 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:56.664
2025-07-01 03:05:56.669 >       yield from g
2025-07-01 03:05:56.673
2025-07-01 03:05:56.677 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:56.682 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:56.686
2025-07-01 03:05:56.691 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:56.696 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:56.700 alo = 504, ahi = 1101
2025-07-01 03:05:56.705 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:56.709 blo = 504, bhi = 1101
2025-07-01 03:05:56.714
2025-07-01 03:05:56.719 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:56.723 r"""
2025-07-01 03:05:56.728 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:56.732 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:56.737 synch point, and intraline difference marking is done on the
2025-07-01 03:05:56.741 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:56.746
2025-07-01 03:05:56.750 Example:
2025-07-01 03:05:56.754
2025-07-01 03:05:56.759 >>> d = Differ()
2025-07-01 03:05:56.763 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:56.767 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:56.772 >>> print(''.join(results), end="")
2025-07-01 03:05:56.776 - abcDefghiJkl
2025-07-01 03:05:56.785 + abcdefGhijkl
2025-07-01 03:05:56.794 """
2025-07-01 03:05:56.798
2025-07-01 03:05:56.803 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:56.807 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:56.811 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:56.816 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:56.820 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:56.825
2025-07-01 03:05:56.829 # search for the pair that matches best without being identical
2025-07-01 03:05:56.834 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:56.838 # on junk -- unless we have to)
2025-07-01 03:05:56.843 for j in range(blo, bhi):
2025-07-01 03:05:56.848 bj = b[j]
2025-07-01 03:05:56.853 cruncher.set_seq2(bj)
2025-07-01 03:05:56.857 for i in range(alo, ahi):
2025-07-01 03:05:56.861 ai = a[i]
2025-07-01 03:05:56.867 if ai == bj:
2025-07-01 03:05:56.872 if eqi is None:
2025-07-01 03:05:56.877 eqi, eqj = i, j
2025-07-01 03:05:56.881 continue
2025-07-01 03:05:56.886 cruncher.set_seq1(ai)
2025-07-01 03:05:56.890 # computing similarity is expensive, so use the quick
2025-07-01 03:05:56.895 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:56.899 # compares by a factor of 3.
2025-07-01 03:05:56.904 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:56.908 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:56.912 # of the computation is cached by cruncher
2025-07-01 03:05:56.917 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:56.921 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:56.926 cruncher.ratio() > best_ratio:
2025-07-01 03:05:56.932 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:56.938 if best_ratio < cutoff:
2025-07-01 03:05:56.945 # no non-identical "pretty close" pair
2025-07-01 03:05:56.950 if eqi is None:
2025-07-01 03:05:56.956 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:56.962 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:56.968 return
2025-07-01 03:05:56.973 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:56.978 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:56.982 else:
2025-07-01 03:05:56.988 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:56.994 eqi = None
2025-07-01 03:05:56.999
2025-07-01 03:05:57.003 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:57.008 # identical
2025-07-01 03:05:57.014
2025-07-01 03:05:57.019 # pump out diffs from before the synch point
2025-07-01 03:05:57.025 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:57.031
2025-07-01 03:05:57.035 # do intraline marking on the synch pair
2025-07-01 03:05:57.040 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:57.044 if eqi is None:
2025-07-01 03:05:57.049 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:57.053 atags = btags = ""
2025-07-01 03:05:57.057 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:57.062 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:57.066 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:57.071 if tag == 'replace':
2025-07-01 03:05:57.075 atags += '^' * la
2025-07-01 03:05:57.080 btags += '^' * lb
2025-07-01 03:05:57.084 elif tag == 'delete':
2025-07-01 03:05:57.089 atags += '-' * la
2025-07-01 03:05:57.093 elif tag == 'insert':
2025-07-01 03:05:57.097 btags += '+' * lb
2025-07-01 03:05:57.101 elif tag == 'equal':
2025-07-01 03:05:57.106 atags += ' ' * la
2025-07-01 03:05:57.111 btags += ' ' * lb
2025-07-01 03:05:57.116 else:
2025-07-01 03:05:57.121 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:57.126 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:57.132 else:
2025-07-01 03:05:57.137 # the synch pair is identical
2025-07-01 03:05:57.142 yield '  ' + aelt
2025-07-01 03:05:57.148
2025-07-01 03:05:57.154 # pump out diffs from after the synch point
2025-07-01 03:05:57.159 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:57.163
2025-07-01 03:05:57.169 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:57.174 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:57.179
2025-07-01 03:05:57.183 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:57.188 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:57.193 alo = 505, ahi = 1101
2025-07-01 03:05:57.198 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:57.202 blo = 505, bhi = 1101
2025-07-01 03:05:57.207
2025-07-01 03:05:57.211 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:57.216 g = []
2025-07-01 03:05:57.221 if alo < ahi:
2025-07-01 03:05:57.225 if blo < bhi:
2025-07-01 03:05:57.230 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:57.234 else:
2025-07-01 03:05:57.239 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:57.244 elif blo < bhi:
2025-07-01 03:05:57.248 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:57.253
2025-07-01 03:05:57.257 >       yield from g
2025-07-01 03:05:57.262
2025-07-01 03:05:57.266 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:57.270 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:57.274
2025-07-01 03:05:57.279 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:57.283 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:57.288 alo = 505, ahi = 1101
2025-07-01 03:05:57.293 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:57.297 blo = 505, bhi = 1101
2025-07-01 03:05:57.302
2025-07-01 03:05:57.307 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:57.311 r"""
2025-07-01 03:05:57.315 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:57.320 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:57.324 synch point, and intraline difference marking is done on the
2025-07-01 03:05:57.328 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:57.332
2025-07-01 03:05:57.337 Example:
2025-07-01 03:05:57.341
2025-07-01 03:05:57.345 >>> d = Differ()
2025-07-01 03:05:57.349 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:57.354 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:57.358 >>> print(''.join(results), end="")
2025-07-01 03:05:57.363 - abcDefghiJkl
2025-07-01 03:05:57.371 + abcdefGhijkl
2025-07-01 03:05:57.380 """
2025-07-01 03:05:57.384
2025-07-01 03:05:57.389 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:57.393 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:57.398 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:57.402 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:57.406 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:57.411
2025-07-01 03:05:57.415 # search for the pair that matches best without being identical
2025-07-01 03:05:57.419 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:57.424 # on junk -- unless we have to)
2025-07-01 03:05:57.428 for j in range(blo, bhi):
2025-07-01 03:05:57.432 bj = b[j]
2025-07-01 03:05:57.437 cruncher.set_seq2(bj)
2025-07-01 03:05:57.441 for i in range(alo, ahi):
2025-07-01 03:05:57.446 ai = a[i]
2025-07-01 03:05:57.450 if ai == bj:
2025-07-01 03:05:57.454 if eqi is None:
2025-07-01 03:05:57.459 eqi, eqj = i, j
2025-07-01 03:05:57.463 continue
2025-07-01 03:05:57.467 cruncher.set_seq1(ai)
2025-07-01 03:05:57.473 # computing similarity is expensive, so use the quick
2025-07-01 03:05:57.478 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:57.484 # compares by a factor of 3.
2025-07-01 03:05:57.491 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:57.497 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:57.503 # of the computation is cached by cruncher
2025-07-01 03:05:57.510 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:57.516 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:57.521 cruncher.ratio() > best_ratio:
2025-07-01 03:05:57.526 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:57.530 if best_ratio < cutoff:
2025-07-01 03:05:57.535 # no non-identical "pretty close" pair
2025-07-01 03:05:57.539 if eqi is None:
2025-07-01 03:05:57.544 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:57.549 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:57.553 return
2025-07-01 03:05:57.557 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:57.562 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:57.566 else:
2025-07-01 03:05:57.571 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:57.575 eqi = None
2025-07-01 03:05:57.580
2025-07-01 03:05:57.585 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:57.589 # identical
2025-07-01 03:05:57.593
2025-07-01 03:05:57.598 # pump out diffs from before the synch point
2025-07-01 03:05:57.602 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:57.606
2025-07-01 03:05:57.610 # do intraline marking on the synch pair
2025-07-01 03:05:57.615 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:57.619 if eqi is None:
2025-07-01 03:05:57.623 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:57.627 atags = btags = ""
2025-07-01 03:05:57.632 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:57.636 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:57.640 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:57.644 if tag == 'replace':
2025-07-01 03:05:57.649 atags += '^' * la
2025-07-01 03:05:57.654 btags += '^' * lb
2025-07-01 03:05:57.658 elif tag == 'delete':
2025-07-01 03:05:57.662 atags += '-' * la
2025-07-01 03:05:57.667 elif tag == 'insert':
2025-07-01 03:05:57.671 btags += '+' * lb
2025-07-01 03:05:57.675 elif tag == 'equal':
2025-07-01 03:05:57.679 atags += ' ' * la
2025-07-01 03:05:57.684 btags += ' ' * lb
2025-07-01 03:05:57.688 else:
2025-07-01 03:05:57.692 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:57.697 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:57.701 else:
2025-07-01 03:05:57.705 # the synch pair is identical
2025-07-01 03:05:57.709 yield '  ' + aelt
2025-07-01 03:05:57.714
2025-07-01 03:05:57.718 # pump out diffs from after the synch point
2025-07-01 03:05:57.723 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:57.727
2025-07-01 03:05:57.731 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:57.736 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:57.740
2025-07-01 03:05:57.744 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:57.749 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:57.753 alo = 506, ahi = 1101
2025-07-01 03:05:57.758 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:57.762 blo = 506, bhi = 1101
2025-07-01 03:05:57.766
2025-07-01 03:05:57.771 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:57.775 g = []
2025-07-01 03:05:57.779 if alo < ahi:
2025-07-01 03:05:57.783 if blo < bhi:
2025-07-01 03:05:57.788 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:57.792 else:
2025-07-01 03:05:57.797 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:57.801 elif blo < bhi:
2025-07-01 03:05:57.806 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:57.810
2025-07-01 03:05:57.814 >       yield from g
2025-07-01 03:05:57.818
2025-07-01 03:05:57.823 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:57.828 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:57.832
2025-07-01 03:05:57.837 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:57.841 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:57.846 alo = 506, ahi = 1101
2025-07-01 03:05:57.850 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:57.855 blo = 506, bhi = 1101
2025-07-01 03:05:57.859
2025-07-01 03:05:57.864 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:57.868 r"""
2025-07-01 03:05:57.873 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:57.879 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:57.883 synch point, and intraline difference marking is done on the
2025-07-01 03:05:57.887 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:57.892
2025-07-01 03:05:57.896 Example:
2025-07-01 03:05:57.900
2025-07-01 03:05:57.904 >>> d = Differ()
2025-07-01 03:05:57.909 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:57.913 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:57.917 >>> print(''.join(results), end="")
2025-07-01 03:05:57.921 - abcDefghiJkl
2025-07-01 03:05:57.930 + abcdefGhijkl
2025-07-01 03:05:57.939 """
2025-07-01 03:05:57.943
2025-07-01 03:05:57.947 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:57.951 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:57.956 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:57.960 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:57.964 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:57.969
2025-07-01 03:05:57.973 # search for the pair that matches best without being identical
2025-07-01 03:05:57.977 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:57.982 # on junk -- unless we have to)
2025-07-01 03:05:57.986 for j in range(blo, bhi):
2025-07-01 03:05:57.990 bj = b[j]
2025-07-01 03:05:57.995 cruncher.set_seq2(bj)
2025-07-01 03:05:57.999 for i in range(alo, ahi):
2025-07-01 03:05:58.004 ai = a[i]
2025-07-01 03:05:58.008 if ai == bj:
2025-07-01 03:05:58.012 if eqi is None:
2025-07-01 03:05:58.017 eqi, eqj = i, j
2025-07-01 03:05:58.021 continue
2025-07-01 03:05:58.025 cruncher.set_seq1(ai)
2025-07-01 03:05:58.030 # computing similarity is expensive, so use the quick
2025-07-01 03:05:58.035 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:58.039 # compares by a factor of 3.
2025-07-01 03:05:58.043 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:58.048 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:58.052 # of the computation is cached by cruncher
2025-07-01 03:05:58.057 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:58.061 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:58.065 cruncher.ratio() > best_ratio:
2025-07-01 03:05:58.070 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:58.074 if best_ratio < cutoff:
2025-07-01 03:05:58.079 # no non-identical "pretty close" pair
2025-07-01 03:05:58.083 if eqi is None:
2025-07-01 03:05:58.087 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:58.092 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:58.096 return
2025-07-01 03:05:58.101 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:58.105 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:58.109 else:
2025-07-01 03:05:58.114 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:58.118 eqi = None
2025-07-01 03:05:58.122
2025-07-01 03:05:58.127 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:58.131 # identical
2025-07-01 03:05:58.136
2025-07-01 03:05:58.140 # pump out diffs from before the synch point
2025-07-01 03:05:58.145 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:58.149
2025-07-01 03:05:58.154 # do intraline marking on the synch pair
2025-07-01 03:05:58.158 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:58.162 if eqi is None:
2025-07-01 03:05:58.167 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:58.171 atags = btags = ""
2025-07-01 03:05:58.176 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:58.180 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:58.185 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:58.189 if tag == 'replace':
2025-07-01 03:05:58.194 atags += '^' * la
2025-07-01 03:05:58.198 btags += '^' * lb
2025-07-01 03:05:58.203 elif tag == 'delete':
2025-07-01 03:05:58.207 atags += '-' * la
2025-07-01 03:05:58.212 elif tag == 'insert':
2025-07-01 03:05:58.216 btags += '+' * lb
2025-07-01 03:05:58.221 elif tag == 'equal':
2025-07-01 03:05:58.225 atags += ' ' * la
2025-07-01 03:05:58.229 btags += ' ' * lb
2025-07-01 03:05:58.233 else:
2025-07-01 03:05:58.238 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:58.242 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:58.246 else:
2025-07-01 03:05:58.250 # the synch pair is identical
2025-07-01 03:05:58.255 yield '  ' + aelt
2025-07-01 03:05:58.259
2025-07-01 03:05:58.264 # pump out diffs from after the synch point
2025-07-01 03:05:58.269 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:58.273
2025-07-01 03:05:58.277 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:58.283 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:58.287
2025-07-01 03:05:58.292 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:58.297 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:58.302 alo = 507, ahi = 1101
2025-07-01 03:05:58.307 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:58.311 blo = 507, bhi = 1101
2025-07-01 03:05:58.315
2025-07-01 03:05:58.320 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:58.324 g = []
2025-07-01 03:05:58.328 if alo < ahi:
2025-07-01 03:05:58.333 if blo < bhi:
2025-07-01 03:05:58.337 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:58.341 else:
2025-07-01 03:05:58.346 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:58.350 elif blo < bhi:
2025-07-01 03:05:58.354 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:58.359
2025-07-01 03:05:58.363 >       yield from g
2025-07-01 03:05:58.368
2025-07-01 03:05:58.372 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:58.376 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:58.381
2025-07-01 03:05:58.385 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:58.390 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:58.394 alo = 507, ahi = 1101
2025-07-01 03:05:58.399 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:58.403 blo = 507, bhi = 1101
2025-07-01 03:05:58.407
2025-07-01 03:05:58.412 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:58.416 r"""
2025-07-01 03:05:58.421 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:58.425 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:58.430 synch point, and intraline difference marking is done on the
2025-07-01 03:05:58.434 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:58.439
2025-07-01 03:05:58.443 Example:
2025-07-01 03:05:58.447
2025-07-01 03:05:58.452 >>> d = Differ()
2025-07-01 03:05:58.456 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:58.461 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:58.465 >>> print(''.join(results), end="")
2025-07-01 03:05:58.470 - abcDefghiJkl
2025-07-01 03:05:58.478 + abcdefGhijkl
2025-07-01 03:05:58.487 """
2025-07-01 03:05:58.491
2025-07-01 03:05:58.496 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:58.501 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:58.505 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:58.510 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:58.515 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:58.519
2025-07-01 03:05:58.524 # search for the pair that matches best without being identical
2025-07-01 03:05:58.528 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:58.533 # on junk -- unless we have to)
2025-07-01 03:05:58.537 for j in range(blo, bhi):
2025-07-01 03:05:58.542 bj = b[j]
2025-07-01 03:05:58.547 cruncher.set_seq2(bj)
2025-07-01 03:05:58.551 for i in range(alo, ahi):
2025-07-01 03:05:58.555 ai = a[i]
2025-07-01 03:05:58.560 if ai == bj:
2025-07-01 03:05:58.565 if eqi is None:
2025-07-01 03:05:58.570 eqi, eqj = i, j
2025-07-01 03:05:58.574 continue
2025-07-01 03:05:58.578 cruncher.set_seq1(ai)
2025-07-01 03:05:58.583 # computing similarity is expensive, so use the quick
2025-07-01 03:05:58.588 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:58.592 # compares by a factor of 3.
2025-07-01 03:05:58.597 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:58.602 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:58.607 # of the computation is cached by cruncher
2025-07-01 03:05:58.613 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:58.617 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:58.622 cruncher.ratio() > best_ratio:
2025-07-01 03:05:58.626 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:58.630 if best_ratio < cutoff:
2025-07-01 03:05:58.635 # no non-identical "pretty close" pair
2025-07-01 03:05:58.639 if eqi is None:
2025-07-01 03:05:58.644 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:58.648 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:58.653 return
2025-07-01 03:05:58.657 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:58.662 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:58.667 else:
2025-07-01 03:05:58.671 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:58.675 eqi = None
2025-07-01 03:05:58.679
2025-07-01 03:05:58.684 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:58.688 # identical
2025-07-01 03:05:58.692
2025-07-01 03:05:58.697 # pump out diffs from before the synch point
2025-07-01 03:05:58.701 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:58.705
2025-07-01 03:05:58.710 # do intraline marking on the synch pair
2025-07-01 03:05:58.714 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:58.718 if eqi is None:
2025-07-01 03:05:58.723 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:58.727 atags = btags = ""
2025-07-01 03:05:58.732 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:58.736 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:58.741 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:58.745 if tag == 'replace':
2025-07-01 03:05:58.749 atags += '^' * la
2025-07-01 03:05:58.753 btags += '^' * lb
2025-07-01 03:05:58.758 elif tag == 'delete':
2025-07-01 03:05:58.762 atags += '-' * la
2025-07-01 03:05:58.766 elif tag == 'insert':
2025-07-01 03:05:58.771 btags += '+' * lb
2025-07-01 03:05:58.775 elif tag == 'equal':
2025-07-01 03:05:58.779 atags += ' ' * la
2025-07-01 03:05:58.784 btags += ' ' * lb
2025-07-01 03:05:58.788 else:
2025-07-01 03:05:58.792 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:58.797 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:58.801 else:
2025-07-01 03:05:58.805 # the synch pair is identical
2025-07-01 03:05:58.809 yield '  ' + aelt
2025-07-01 03:05:58.814
2025-07-01 03:05:58.818 # pump out diffs from after the synch point
2025-07-01 03:05:58.822 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:58.827
2025-07-01 03:05:58.831 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:58.835 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:58.839
2025-07-01 03:05:58.844 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:58.848 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:58.852 alo = 510, ahi = 1101
2025-07-01 03:05:58.857 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:58.861 blo = 510, bhi = 1101
2025-07-01 03:05:58.866
2025-07-01 03:05:58.870 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:58.874 g = []
2025-07-01 03:05:58.878 if alo < ahi:
2025-07-01 03:05:58.883 if blo < bhi:
2025-07-01 03:05:58.887 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:58.891 else:
2025-07-01 03:05:58.895 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:58.900 elif blo < bhi:
2025-07-01 03:05:58.904 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:58.908
2025-07-01 03:05:58.913 >       yield from g
2025-07-01 03:05:58.917
2025-07-01 03:05:58.921 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:58.926 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:58.930
2025-07-01 03:05:58.934 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:58.939 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:58.943 alo = 510, ahi = 1101
2025-07-01 03:05:58.948 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:58.952 blo = 510, bhi = 1101
2025-07-01 03:05:58.956
2025-07-01 03:05:58.961 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:58.965 r"""
2025-07-01 03:05:58.969 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:58.974 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:58.978 synch point, and intraline difference marking is done on the
2025-07-01 03:05:58.982 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:58.987
2025-07-01 03:05:58.991 Example:
2025-07-01 03:05:58.996
2025-07-01 03:05:59.001 >>> d = Differ()
2025-07-01 03:05:59.006 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:59.010 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:59.015 >>> print(''.join(results), end="")
2025-07-01 03:05:59.020 - abcDefghiJkl
2025-07-01 03:05:59.029 + abcdefGhijkl
2025-07-01 03:05:59.038 """
2025-07-01 03:05:59.042
2025-07-01 03:05:59.047 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:59.051 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:59.056 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:59.060 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:59.065 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:59.069
2025-07-01 03:05:59.074 # search for the pair that matches best without being identical
2025-07-01 03:05:59.078 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:59.083 # on junk -- unless we have to)
2025-07-01 03:05:59.087 for j in range(blo, bhi):
2025-07-01 03:05:59.092 bj = b[j]
2025-07-01 03:05:59.096 cruncher.set_seq2(bj)
2025-07-01 03:05:59.101 for i in range(alo, ahi):
2025-07-01 03:05:59.105 ai = a[i]
2025-07-01 03:05:59.110 if ai == bj:
2025-07-01 03:05:59.117 if eqi is None:
2025-07-01 03:05:59.123 eqi, eqj = i, j
2025-07-01 03:05:59.128 continue
2025-07-01 03:05:59.134 cruncher.set_seq1(ai)
2025-07-01 03:05:59.139 # computing similarity is expensive, so use the quick
2025-07-01 03:05:59.144 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:59.149 # compares by a factor of 3.
2025-07-01 03:05:59.154 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:59.159 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:59.163 # of the computation is cached by cruncher
2025-07-01 03:05:59.168 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:59.172 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:59.177 cruncher.ratio() > best_ratio:
2025-07-01 03:05:59.181 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:59.186 if best_ratio < cutoff:
2025-07-01 03:05:59.191 # no non-identical "pretty close" pair
2025-07-01 03:05:59.196 if eqi is None:
2025-07-01 03:05:59.202 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:59.206 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:59.211 return
2025-07-01 03:05:59.215 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:59.220 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:59.224 else:
2025-07-01 03:05:59.229 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:59.233 eqi = None
2025-07-01 03:05:59.238
2025-07-01 03:05:59.242 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:59.247 # identical
2025-07-01 03:05:59.251
2025-07-01 03:05:59.256 # pump out diffs from before the synch point
2025-07-01 03:05:59.260 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:59.265
2025-07-01 03:05:59.269 # do intraline marking on the synch pair
2025-07-01 03:05:59.274 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:59.278 if eqi is None:
2025-07-01 03:05:59.282 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:59.287 atags = btags = ""
2025-07-01 03:05:59.291 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:59.296 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:59.300 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:59.305 if tag == 'replace':
2025-07-01 03:05:59.309 atags += '^' * la
2025-07-01 03:05:59.314 btags += '^' * lb
2025-07-01 03:05:59.318 elif tag == 'delete':
2025-07-01 03:05:59.323 atags += '-' * la
2025-07-01 03:05:59.327 elif tag == 'insert':
2025-07-01 03:05:59.332 btags += '+' * lb
2025-07-01 03:05:59.336 elif tag == 'equal':
2025-07-01 03:05:59.341 atags += ' ' * la
2025-07-01 03:05:59.346 btags += ' ' * lb
2025-07-01 03:05:59.350 else:
2025-07-01 03:05:59.355 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:59.360 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:59.365 else:
2025-07-01 03:05:59.369 # the synch pair is identical
2025-07-01 03:05:59.374 yield '  ' + aelt
2025-07-01 03:05:59.378
2025-07-01 03:05:59.383 # pump out diffs from after the synch point
2025-07-01 03:05:59.388 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:59.392
2025-07-01 03:05:59.396 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:59.401 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:59.405
2025-07-01 03:05:59.409 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:59.414 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:59.418 alo = 511, ahi = 1101
2025-07-01 03:05:59.423 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:59.427 blo = 511, bhi = 1101
2025-07-01 03:05:59.431
2025-07-01 03:05:59.436 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:59.440 g = []
2025-07-01 03:05:59.444 if alo < ahi:
2025-07-01 03:05:59.448 if blo < bhi:
2025-07-01 03:05:59.453 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:59.457 else:
2025-07-01 03:05:59.461 g = self._dump('-', a, alo, ahi)
2025-07-01 03:05:59.465 elif blo < bhi:
2025-07-01 03:05:59.469 g = self._dump('+', b, blo, bhi)
2025-07-01 03:05:59.474
2025-07-01 03:05:59.478 >       yield from g
2025-07-01 03:05:59.482
2025-07-01 03:05:59.486 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:05:59.491 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:59.495
2025-07-01 03:05:59.499 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:59.504 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:59.508 alo = 511, ahi = 1101
2025-07-01 03:05:59.513 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:59.518 blo = 511, bhi = 1101
2025-07-01 03:05:59.522
2025-07-01 03:05:59.526 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:59.531 r"""
2025-07-01 03:05:59.536 When replacing one block of lines with another, search the blocks
2025-07-01 03:05:59.541 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:05:59.546 synch point, and intraline difference marking is done on the
2025-07-01 03:05:59.551 similar pair. Lots of work, but often worth it.
2025-07-01 03:05:59.555
2025-07-01 03:05:59.560 Example:
2025-07-01 03:05:59.565
2025-07-01 03:05:59.569 >>> d = Differ()
2025-07-01 03:05:59.574 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:05:59.578 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:05:59.582 >>> print(''.join(results), end="")
2025-07-01 03:05:59.586 - abcDefghiJkl
2025-07-01 03:05:59.595 + abcdefGhijkl
2025-07-01 03:05:59.604 """
2025-07-01 03:05:59.608
2025-07-01 03:05:59.613 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:05:59.617 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:05:59.621 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:05:59.626 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:05:59.630 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:05:59.635
2025-07-01 03:05:59.640 # search for the pair that matches best without being identical
2025-07-01 03:05:59.645 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:05:59.649 # on junk -- unless we have to)
2025-07-01 03:05:59.654 for j in range(blo, bhi):
2025-07-01 03:05:59.658 bj = b[j]
2025-07-01 03:05:59.662 cruncher.set_seq2(bj)
2025-07-01 03:05:59.667 for i in range(alo, ahi):
2025-07-01 03:05:59.671 ai = a[i]
2025-07-01 03:05:59.675 if ai == bj:
2025-07-01 03:05:59.680 if eqi is None:
2025-07-01 03:05:59.684 eqi, eqj = i, j
2025-07-01 03:05:59.688 continue
2025-07-01 03:05:59.693 cruncher.set_seq1(ai)
2025-07-01 03:05:59.697 # computing similarity is expensive, so use the quick
2025-07-01 03:05:59.702 # upper bounds first -- have seen this speed up messy
2025-07-01 03:05:59.706 # compares by a factor of 3.
2025-07-01 03:05:59.710 # note that ratio() is only expensive to compute the first
2025-07-01 03:05:59.715 # time it's called on a sequence pair; the expensive part
2025-07-01 03:05:59.719 # of the computation is cached by cruncher
2025-07-01 03:05:59.723 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:05:59.728 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:05:59.732 cruncher.ratio() > best_ratio:
2025-07-01 03:05:59.737 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:05:59.741 if best_ratio < cutoff:
2025-07-01 03:05:59.745 # no non-identical "pretty close" pair
2025-07-01 03:05:59.749 if eqi is None:
2025-07-01 03:05:59.754 # no identical pair either -- treat it as a straight replace
2025-07-01 03:05:59.758 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:05:59.762 return
2025-07-01 03:05:59.767 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:05:59.771 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:05:59.775 else:
2025-07-01 03:05:59.780 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:05:59.785 eqi = None
2025-07-01 03:05:59.789
2025-07-01 03:05:59.794 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:05:59.798 # identical
2025-07-01 03:05:59.802
2025-07-01 03:05:59.806 # pump out diffs from before the synch point
2025-07-01 03:05:59.811 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:05:59.815
2025-07-01 03:05:59.820 # do intraline marking on the synch pair
2025-07-01 03:05:59.824 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:05:59.830 if eqi is None:
2025-07-01 03:05:59.834 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:05:59.838 atags = btags = ""
2025-07-01 03:05:59.843 cruncher.set_seqs(aelt, belt)
2025-07-01 03:05:59.847 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:05:59.852 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:05:59.856 if tag == 'replace':
2025-07-01 03:05:59.861 atags += '^' * la
2025-07-01 03:05:59.865 btags += '^' * lb
2025-07-01 03:05:59.870 elif tag == 'delete':
2025-07-01 03:05:59.875 atags += '-' * la
2025-07-01 03:05:59.879 elif tag == 'insert':
2025-07-01 03:05:59.883 btags += '+' * lb
2025-07-01 03:05:59.888 elif tag == 'equal':
2025-07-01 03:05:59.892 atags += ' ' * la
2025-07-01 03:05:59.896 btags += ' ' * lb
2025-07-01 03:05:59.900 else:
2025-07-01 03:05:59.905 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:05:59.909 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:05:59.913 else:
2025-07-01 03:05:59.918 # the synch pair is identical
2025-07-01 03:05:59.922 yield '  ' + aelt
2025-07-01 03:05:59.927
2025-07-01 03:05:59.931 # pump out diffs from after the synch point
2025-07-01 03:05:59.936 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:05:59.940
2025-07-01 03:05:59.945 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:05:59.949 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:05:59.954
2025-07-01 03:05:59.958 self = <difflib.Differ object at [hex]>
2025-07-01 03:05:59.963 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:05:59.967 alo = 512, ahi = 1101
2025-07-01 03:05:59.972 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:05:59.976 blo = 512, bhi = 1101
2025-07-01 03:05:59.980
2025-07-01 03:05:59.984 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:05:59.989 g = []
2025-07-01 03:05:59.993 if alo < ahi:
2025-07-01 03:05:59.997 if blo < bhi:
2025-07-01 03:06:00.002 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:00.006 else:
2025-07-01 03:06:00.012 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:00.018 elif blo < bhi:
2025-07-01 03:06:00.022 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:00.027
2025-07-01 03:06:00.031 >       yield from g
2025-07-01 03:06:00.035
2025-07-01 03:06:00.040 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:00.045 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:00.049
2025-07-01 03:06:00.054 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:00.058 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:00.063 alo = 512, ahi = 1101
2025-07-01 03:06:00.068 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:00.072 blo = 512, bhi = 1101
2025-07-01 03:06:00.077
2025-07-01 03:06:00.081 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:00.085 r"""
2025-07-01 03:06:00.090 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:00.095 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:00.099 synch point, and intraline difference marking is done on the
2025-07-01 03:06:00.103 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:00.108
2025-07-01 03:06:00.112 Example:
2025-07-01 03:06:00.116
2025-07-01 03:06:00.121 >>> d = Differ()
2025-07-01 03:06:00.125 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:00.129 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:00.134 >>> print(''.join(results), end="")
2025-07-01 03:06:00.138 - abcDefghiJkl
2025-07-01 03:06:00.147 + abcdefGhijkl
2025-07-01 03:06:00.155 """
2025-07-01 03:06:00.160
2025-07-01 03:06:00.165 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:00.169 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:00.174 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:00.178 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:00.183 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:00.187
2025-07-01 03:06:00.191 # search for the pair that matches best without being identical
2025-07-01 03:06:00.196 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:00.201 # on junk -- unless we have to)
2025-07-01 03:06:00.206 for j in range(blo, bhi):
2025-07-01 03:06:00.210 bj = b[j]
2025-07-01 03:06:00.215 cruncher.set_seq2(bj)
2025-07-01 03:06:00.219 for i in range(alo, ahi):
2025-07-01 03:06:00.223 ai = a[i]
2025-07-01 03:06:00.228 if ai == bj:
2025-07-01 03:06:00.232 if eqi is None:
2025-07-01 03:06:00.237 eqi, eqj = i, j
2025-07-01 03:06:00.241 continue
2025-07-01 03:06:00.245 cruncher.set_seq1(ai)
2025-07-01 03:06:00.250 # computing similarity is expensive, so use the quick
2025-07-01 03:06:00.254 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:00.259 # compares by a factor of 3.
2025-07-01 03:06:00.263 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:00.268 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:00.272 # of the computation is cached by cruncher
2025-07-01 03:06:00.277 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:00.282 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:00.286 cruncher.ratio() > best_ratio:
2025-07-01 03:06:00.290 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:00.295 if best_ratio < cutoff:
2025-07-01 03:06:00.299 # no non-identical "pretty close" pair
2025-07-01 03:06:00.304 if eqi is None:
2025-07-01 03:06:00.308 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:00.313 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:00.317 return
2025-07-01 03:06:00.321 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:00.327 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:00.333 else:
2025-07-01 03:06:00.340 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:00.346 eqi = None
2025-07-01 03:06:00.352
2025-07-01 03:06:00.359 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:00.365 # identical
2025-07-01 03:06:00.370
2025-07-01 03:06:00.376 # pump out diffs from before the synch point
2025-07-01 03:06:00.381 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:00.385
2025-07-01 03:06:00.390 # do intraline marking on the synch pair
2025-07-01 03:06:00.394 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:00.399 if eqi is None:
2025-07-01 03:06:00.403 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:00.408 atags = btags = ""
2025-07-01 03:06:00.412 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:00.416 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:00.421 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:00.425 if tag == 'replace':
2025-07-01 03:06:00.430 atags += '^' * la
2025-07-01 03:06:00.435 btags += '^' * lb
2025-07-01 03:06:00.440 elif tag == 'delete':
2025-07-01 03:06:00.444 atags += '-' * la
2025-07-01 03:06:00.449 elif tag == 'insert':
2025-07-01 03:06:00.453 btags += '+' * lb
2025-07-01 03:06:00.458 elif tag == 'equal':
2025-07-01 03:06:00.462 atags += ' ' * la
2025-07-01 03:06:00.467 btags += ' ' * lb
2025-07-01 03:06:00.472 else:
2025-07-01 03:06:00.476 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:00.481 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:00.485 else:
2025-07-01 03:06:00.489 # the synch pair is identical
2025-07-01 03:06:00.493 yield '  ' + aelt
2025-07-01 03:06:00.498
2025-07-01 03:06:00.502 # pump out diffs from after the synch point
2025-07-01 03:06:00.507 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:00.511
2025-07-01 03:06:00.515 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:00.519 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:00.524
2025-07-01 03:06:00.528 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:00.532 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:00.537 alo = 513, ahi = 1101
2025-07-01 03:06:00.541 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:00.546 blo = 513, bhi = 1101
2025-07-01 03:06:00.550
2025-07-01 03:06:00.554 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:00.558 g = []
2025-07-01 03:06:00.562 if alo < ahi:
2025-07-01 03:06:00.567 if blo < bhi:
2025-07-01 03:06:00.571 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:00.575 else:
2025-07-01 03:06:00.579 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:00.584 elif blo < bhi:
2025-07-01 03:06:00.588 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:00.592
2025-07-01 03:06:00.597 >       yield from g
2025-07-01 03:06:00.601
2025-07-01 03:06:00.605 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:00.610 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:00.614
2025-07-01 03:06:00.623 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:00.638 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:00.644 alo = 513, ahi = 1101
2025-07-01 03:06:00.649 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:00.653 blo = 513, bhi = 1101
2025-07-01 03:06:00.657
2025-07-01 03:06:00.662 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:00.667 r"""
2025-07-01 03:06:00.671 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:00.676 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:00.680 synch point, and intraline difference marking is done on the
2025-07-01 03:06:00.685 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:00.689
2025-07-01 03:06:00.693 Example:
2025-07-01 03:06:00.697
2025-07-01 03:06:00.702 >>> d = Differ()
2025-07-01 03:06:00.706 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:00.710 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:00.715 >>> print(''.join(results), end="")
2025-07-01 03:06:00.719 - abcDefghiJkl
2025-07-01 03:06:00.728 + abcdefGhijkl
2025-07-01 03:06:00.736 """
2025-07-01 03:06:00.741
2025-07-01 03:06:00.745 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:00.750 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:00.755 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:00.759 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:00.764 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:00.768
2025-07-01 03:06:00.773 # search for the pair that matches best without being identical
2025-07-01 03:06:00.777 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:00.782 # on junk -- unless we have to)
2025-07-01 03:06:00.786 for j in range(blo, bhi):
2025-07-01 03:06:00.790 bj = b[j]
2025-07-01 03:06:00.795 cruncher.set_seq2(bj)
2025-07-01 03:06:00.799 for i in range(alo, ahi):
2025-07-01 03:06:00.804 ai = a[i]
2025-07-01 03:06:00.809 if ai == bj:
2025-07-01 03:06:00.813 if eqi is None:
2025-07-01 03:06:00.818 eqi, eqj = i, j
2025-07-01 03:06:00.822 continue
2025-07-01 03:06:00.826 cruncher.set_seq1(ai)
2025-07-01 03:06:00.830 # computing similarity is expensive, so use the quick
2025-07-01 03:06:00.835 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:00.839 # compares by a factor of 3.
2025-07-01 03:06:00.844 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:00.848 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:00.852 # of the computation is cached by cruncher
2025-07-01 03:06:00.857 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:00.861 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:00.866 cruncher.ratio() > best_ratio:
2025-07-01 03:06:00.870 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:00.874 if best_ratio < cutoff:
2025-07-01 03:06:00.879 # no non-identical "pretty close" pair
2025-07-01 03:06:00.883 if eqi is None:
2025-07-01 03:06:00.887 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:00.892 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:00.896 return
2025-07-01 03:06:00.901 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:00.905 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:00.910 else:
2025-07-01 03:06:00.915 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:00.919 eqi = None
2025-07-01 03:06:00.924
2025-07-01 03:06:00.928 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:00.932 # identical
2025-07-01 03:06:00.937
2025-07-01 03:06:00.941 # pump out diffs from before the synch point
2025-07-01 03:06:00.946 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:00.950
2025-07-01 03:06:00.954 # do intraline marking on the synch pair
2025-07-01 03:06:00.959 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:00.963 if eqi is None:
2025-07-01 03:06:00.967 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:00.972 atags = btags = ""
2025-07-01 03:06:00.976 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:00.981 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:00.985 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:00.990 if tag == 'replace':
2025-07-01 03:06:00.994 atags += '^' * la
2025-07-01 03:06:00.998 btags += '^' * lb
2025-07-01 03:06:01.003 elif tag == 'delete':
2025-07-01 03:06:01.007 atags += '-' * la
2025-07-01 03:06:01.012 elif tag == 'insert':
2025-07-01 03:06:01.016 btags += '+' * lb
2025-07-01 03:06:01.020 elif tag == 'equal':
2025-07-01 03:06:01.025 atags += ' ' * la
2025-07-01 03:06:01.029 btags += ' ' * lb
2025-07-01 03:06:01.034 else:
2025-07-01 03:06:01.039 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:01.044 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:01.048 else:
2025-07-01 03:06:01.052 # the synch pair is identical
2025-07-01 03:06:01.057 yield '  ' + aelt
2025-07-01 03:06:01.061
2025-07-01 03:06:01.065 # pump out diffs from after the synch point
2025-07-01 03:06:01.070 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:01.074
2025-07-01 03:06:01.080 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:01.084 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:01.088
2025-07-01 03:06:01.093 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:01.098 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:01.104 alo = 514, ahi = 1101
2025-07-01 03:06:01.109 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:01.115 blo = 514, bhi = 1101
2025-07-01 03:06:01.119
2025-07-01 03:06:01.124 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:01.128 g = []
2025-07-01 03:06:01.133 if alo < ahi:
2025-07-01 03:06:01.137 if blo < bhi:
2025-07-01 03:06:01.143 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:01.148 else:
2025-07-01 03:06:01.153 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:01.158 elif blo < bhi:
2025-07-01 03:06:01.163 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:01.167
2025-07-01 03:06:01.171 >       yield from g
2025-07-01 03:06:01.177
2025-07-01 03:06:01.183 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:01.188 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:01.193
2025-07-01 03:06:01.200 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:01.206 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:01.211 alo = 514, ahi = 1101
2025-07-01 03:06:01.217 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:01.221 blo = 514, bhi = 1101
2025-07-01 03:06:01.225
2025-07-01 03:06:01.231 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:01.235 r"""
2025-07-01 03:06:01.240 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:01.245 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:01.250 synch point, and intraline difference marking is done on the
2025-07-01 03:06:01.256 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:01.261
2025-07-01 03:06:01.266 Example:
2025-07-01 03:06:01.271
2025-07-01 03:06:01.276 >>> d = Differ()
2025-07-01 03:06:01.294 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:01.302 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:01.314 >>> print(''.join(results), end="")
2025-07-01 03:06:01.333 - abcDefghiJkl
2025-07-01 03:06:01.354 + abcdefGhijkl
2025-07-01 03:06:01.374 """
2025-07-01 03:06:01.386
2025-07-01 03:06:01.394 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:01.410 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:01.422 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:01.429 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:01.440 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:01.454
2025-07-01 03:06:01.465 # search for the pair that matches best without being identical
2025-07-01 03:06:01.477 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:01.490 # on junk -- unless we have to)
2025-07-01 03:06:01.503 for j in range(blo, bhi):
2025-07-01 03:06:01.514 bj = b[j]
2025-07-01 03:06:01.525 cruncher.set_seq2(bj)
2025-07-01 03:06:01.542 for i in range(alo, ahi):
2025-07-01 03:06:01.550 ai = a[i]
2025-07-01 03:06:01.562 if ai == bj:
2025-07-01 03:06:01.574 if eqi is None:
2025-07-01 03:06:01.582 eqi, eqj = i, j
2025-07-01 03:06:01.594 continue
2025-07-01 03:06:01.604 cruncher.set_seq1(ai)
2025-07-01 03:06:01.614 # computing similarity is expensive, so use the quick
2025-07-01 03:06:01.623 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:01.634 # compares by a factor of 3.
2025-07-01 03:06:01.645 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:01.651 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:01.669 # of the computation is cached by cruncher
2025-07-01 03:06:01.679 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:01.697 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:01.709 cruncher.ratio() > best_ratio:
2025-07-01 03:06:01.721 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:01.733 if best_ratio < cutoff:
2025-07-01 03:06:01.742 # no non-identical "pretty close" pair
2025-07-01 03:06:01.758 if eqi is None:
2025-07-01 03:06:01.770 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:01.781 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:01.802 return
2025-07-01 03:06:01.810 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:01.818 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:01.830 else:
2025-07-01 03:06:01.842 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:01.854 eqi = None
2025-07-01 03:06:01.862
2025-07-01 03:06:01.874 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:01.882 # identical
2025-07-01 03:06:01.894
2025-07-01 03:06:01.906 # pump out diffs from before the synch point
2025-07-01 03:06:01.915 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:01.930
2025-07-01 03:06:01.938 # do intraline marking on the synch pair
2025-07-01 03:06:01.948 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:01.962 if eqi is None:
2025-07-01 03:06:01.970 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:01.978 atags = btags = ""
2025-07-01 03:06:01.990 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:02.002 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:02.014 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:02.026 if tag == 'replace':
2025-07-01 03:06:02.034 atags += '^' * la
2025-07-01 03:06:02.045 btags += '^' * lb
2025-07-01 03:06:02.058 elif tag == 'delete':
2025-07-01 03:06:02.066 atags += '-' * la
2025-07-01 03:06:02.082 elif tag == 'insert':
2025-07-01 03:06:02.090 btags += '+' * lb
2025-07-01 03:06:02.106 elif tag == 'equal':
2025-07-01 03:06:02.114 atags += ' ' * la
2025-07-01 03:06:02.130 btags += ' ' * lb
2025-07-01 03:06:02.138 else:
2025-07-01 03:06:02.154 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:02.161 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:02.178 else:
2025-07-01 03:06:02.186 # the synch pair is identical
2025-07-01 03:06:02.202 yield '  ' + aelt
2025-07-01 03:06:02.210
2025-07-01 03:06:02.226 # pump out diffs from after the synch point
2025-07-01 03:06:02.238 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:02.248
2025-07-01 03:06:02.263 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:02.275 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:02.285
2025-07-01 03:06:02.294 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:02.310 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:02.321 alo = 515, ahi = 1101
2025-07-01 03:06:02.334 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:02.341 blo = 515, bhi = 1101
2025-07-01 03:06:02.358
2025-07-01 03:06:02.366 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:02.373 g = []
2025-07-01 03:06:02.383 if alo < ahi:
2025-07-01 03:06:02.395 if blo < bhi:
2025-07-01 03:06:02.410 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:02.422 else:
2025-07-01 03:06:02.438 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:02.447 elif blo < bhi:
2025-07-01 03:06:02.458 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:02.474
2025-07-01 03:06:02.486 >       yield from g
2025-07-01 03:06:02.497
2025-07-01 03:06:02.510 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:02.522 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:02.534
2025-07-01 03:06:02.550 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:02.563 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:02.572 alo = 515, ahi = 1101
2025-07-01 03:06:02.586 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:02.602 blo = 515, bhi = 1101
2025-07-01 03:06:02.611
2025-07-01 03:06:02.626 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:02.634 r"""
2025-07-01 03:06:02.646 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:02.658 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:02.666 synch point, and intraline difference marking is done on the
2025-07-01 03:06:02.678 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:02.696
2025-07-01 03:06:02.705 Example:
2025-07-01 03:06:02.713
2025-07-01 03:06:02.722 >>> d = Differ()
2025-07-01 03:06:02.734 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:02.746 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:02.754 >>> print(''.join(results), end="")
2025-07-01 03:06:02.761 - abcDefghiJkl
2025-07-01 03:06:02.771 + abcdefGhijkl
2025-07-01 03:06:02.781 """
2025-07-01 03:06:02.786
2025-07-01 03:06:02.790 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:02.797 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:02.802 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:02.807 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:02.817 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:02.822
2025-07-01 03:06:02.827 # search for the pair that matches best without being identical
2025-07-01 03:06:02.832 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:02.836 # on junk -- unless we have to)
2025-07-01 03:06:02.841 for j in range(blo, bhi):
2025-07-01 03:06:02.845 bj = b[j]
2025-07-01 03:06:02.850 cruncher.set_seq2(bj)
2025-07-01 03:06:02.855 for i in range(alo, ahi):
2025-07-01 03:06:02.859 ai = a[i]
2025-07-01 03:06:02.864 if ai == bj:
2025-07-01 03:06:02.869 if eqi is None:
2025-07-01 03:06:02.873 eqi, eqj = i, j
2025-07-01 03:06:02.878 continue
2025-07-01 03:06:02.882 cruncher.set_seq1(ai)
2025-07-01 03:06:02.887 # computing similarity is expensive, so use the quick
2025-07-01 03:06:02.892 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:02.897 # compares by a factor of 3.
2025-07-01 03:06:02.902 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:02.909 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:02.930 # of the computation is cached by cruncher
2025-07-01 03:06:02.938 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:02.950 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:02.958 cruncher.ratio() > best_ratio:
2025-07-01 03:06:02.970 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:02.978 if best_ratio < cutoff:
2025-07-01 03:06:02.990 # no non-identical "pretty close" pair
2025-07-01 03:06:02.998 if eqi is None:
2025-07-01 03:06:03.012 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:03.026 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:03.034 return
2025-07-01 03:06:03.046 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:03.054 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:03.066 else:
2025-07-01 03:06:03.074 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:03.084 eqi = None
2025-07-01 03:06:03.094
2025-07-01 03:06:03.105 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:03.120 # identical
2025-07-01 03:06:03.130
2025-07-01 03:06:03.142 # pump out diffs from before the synch point
2025-07-01 03:06:03.150 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:03.166
2025-07-01 03:06:03.174 # do intraline marking on the synch pair
2025-07-01 03:06:03.190 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:03.198 if eqi is None:
2025-07-01 03:06:03.210 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:03.218 atags = btags = ""
2025-07-01 03:06:03.226 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:03.242 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:03.258 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:03.274 if tag == 'replace':
2025-07-01 03:06:03.282 atags += '^' * la
2025-07-01 03:06:03.294 btags += '^' * lb
2025-07-01 03:06:03.301 elif tag == 'delete':
2025-07-01 03:06:03.314 atags += '-' * la
2025-07-01 03:06:03.321 elif tag == 'insert':
2025-07-01 03:06:03.330 btags += '+' * lb
2025-07-01 03:06:03.344 elif tag == 'equal':
2025-07-01 03:06:03.357 atags += ' ' * la
2025-07-01 03:06:03.361 btags += ' ' * lb
2025-07-01 03:06:03.366 else:
2025-07-01 03:06:03.370 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:03.374 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:03.379 else:
2025-07-01 03:06:03.383 # the synch pair is identical
2025-07-01 03:06:03.387 yield '  ' + aelt
2025-07-01 03:06:03.392
2025-07-01 03:06:03.398 # pump out diffs from after the synch point
2025-07-01 03:06:03.403 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:03.407
2025-07-01 03:06:03.412 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:03.416 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:03.421
2025-07-01 03:06:03.432 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:03.437 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:03.442 alo = 516, ahi = 1101
2025-07-01 03:06:03.447 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:03.451 blo = 516, bhi = 1101
2025-07-01 03:06:03.455
2025-07-01 03:06:03.460 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:03.464 g = []
2025-07-01 03:06:03.468 if alo < ahi:
2025-07-01 03:06:03.473 if blo < bhi:
2025-07-01 03:06:03.477 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:03.481 else:
2025-07-01 03:06:03.486 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:03.490 elif blo < bhi:
2025-07-01 03:06:03.494 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:03.500
2025-07-01 03:06:03.505 >       yield from g
2025-07-01 03:06:03.509
2025-07-01 03:06:03.513 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:03.518 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:03.522
2025-07-01 03:06:03.527 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:03.532 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:03.536 alo = 516, ahi = 1101
2025-07-01 03:06:03.541 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:03.546 blo = 516, bhi = 1101
2025-07-01 03:06:03.550
2025-07-01 03:06:03.554 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:03.570 r"""
2025-07-01 03:06:03.582 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:03.590 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:03.602 synch point, and intraline difference marking is done on the
2025-07-01 03:06:03.614 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:03.630
2025-07-01 03:06:03.642 Example:
2025-07-01 03:06:03.654
2025-07-01 03:06:03.662 >>> d = Differ()
2025-07-01 03:06:03.674 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:03.690 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:03.706 >>> print(''.join(results), end="")
2025-07-01 03:06:03.721 - abcDefghiJkl
2025-07-01 03:06:03.745 + abcdefGhijkl
2025-07-01 03:06:03.767 """
2025-07-01 03:06:03.776
2025-07-01 03:06:03.781 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:03.795 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:03.806 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:03.813 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:03.826 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:03.837
2025-07-01 03:06:03.848 # search for the pair that matches best without being identical
2025-07-01 03:06:03.864 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:03.874 # on junk -- unless we have to)
2025-07-01 03:06:03.879 for j in range(blo, bhi):
2025-07-01 03:06:03.886 bj = b[j]
2025-07-01 03:06:03.892 cruncher.set_seq2(bj)
2025-07-01 03:06:03.897 for i in range(alo, ahi):
2025-07-01 03:06:03.901 ai = a[i]
2025-07-01 03:06:03.905 if ai == bj:
2025-07-01 03:06:03.910 if eqi is None:
2025-07-01 03:06:03.914 eqi, eqj = i, j
2025-07-01 03:06:03.919 continue
2025-07-01 03:06:03.923 cruncher.set_seq1(ai)
2025-07-01 03:06:03.928 # computing similarity is expensive, so use the quick
2025-07-01 03:06:03.933 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:03.937 # compares by a factor of 3.
2025-07-01 03:06:03.942 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:03.946 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:03.950 # of the computation is cached by cruncher
2025-07-01 03:06:03.955 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:03.960 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:03.964 cruncher.ratio() > best_ratio:
2025-07-01 03:06:03.969 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:03.973 if best_ratio < cutoff:
2025-07-01 03:06:03.990 # no non-identical "pretty close" pair
2025-07-01 03:06:03.998 if eqi is None:
2025-07-01 03:06:04.010 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:04.022 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:04.034 return
2025-07-01 03:06:04.042 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:04.053 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:04.065 else:
2025-07-01 03:06:04.071 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:04.086 eqi = None
2025-07-01 03:06:04.097
2025-07-01 03:06:04.110 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:04.118 # identical
2025-07-01 03:06:04.126
2025-07-01 03:06:04.138 # pump out diffs from before the synch point
2025-07-01 03:06:04.145 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:04.162
2025-07-01 03:06:04.169 # do intraline marking on the synch pair
2025-07-01 03:06:04.177 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:04.200 if eqi is None:
2025-07-01 03:06:04.214 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:04.222 atags = btags = ""
2025-07-01 03:06:04.230 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:04.242 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:04.252 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:04.266 if tag == 'replace':
2025-07-01 03:06:04.277 atags += '^' * la
2025-07-01 03:06:04.285 btags += '^' * lb
2025-07-01 03:06:04.299 elif tag == 'delete':
2025-07-01 03:06:04.309 atags += '-' * la
2025-07-01 03:06:04.316 elif tag == 'insert':
2025-07-01 03:06:04.323 btags += '+' * lb
2025-07-01 03:06:04.333 elif tag == 'equal':
2025-07-01 03:06:04.345 atags += ' ' * la
2025-07-01 03:06:04.358 btags += ' ' * lb
2025-07-01 03:06:04.365 else:
2025-07-01 03:06:04.374 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:04.383 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:04.394 else:
2025-07-01 03:06:04.406 # the synch pair is identical
2025-07-01 03:06:04.418 yield '  ' + aelt
2025-07-01 03:06:04.430
2025-07-01 03:06:04.438 # pump out diffs from after the synch point
2025-07-01 03:06:04.445 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:04.453
2025-07-01 03:06:04.466 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:04.477 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:04.490
2025-07-01 03:06:04.501 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:04.514 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:04.526 alo = 517, ahi = 1101
2025-07-01 03:06:04.541 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:04.553 blo = 517, bhi = 1101
2025-07-01 03:06:04.566
2025-07-01 03:06:04.578 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:04.590 g = []
2025-07-01 03:06:04.601 if alo < ahi:
2025-07-01 03:06:04.610 if blo < bhi:
2025-07-01 03:06:04.620 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:04.630 else:
2025-07-01 03:06:04.639 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:04.656 elif blo < bhi:
2025-07-01 03:06:04.669 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:04.682
2025-07-01 03:06:04.693 >       yield from g
2025-07-01 03:06:04.706
2025-07-01 03:06:04.718 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:04.725 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:04.735
2025-07-01 03:06:04.750 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:04.766 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:04.773 alo = 517, ahi = 1101
2025-07-01 03:06:04.786 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:04.798 blo = 517, bhi = 1101
2025-07-01 03:06:04.806
2025-07-01 03:06:04.814 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:04.826 r"""
2025-07-01 03:06:04.834 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:04.846 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:04.854 synch point, and intraline difference marking is done on the
2025-07-01 03:06:04.870 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:04.878
2025-07-01 03:06:04.894 Example:
2025-07-01 03:06:04.902
2025-07-01 03:06:04.914 >>> d = Differ()
2025-07-01 03:06:04.922 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:04.934 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:04.942 >>> print(''.join(results), end="")
2025-07-01 03:06:04.956 - abcDefghiJkl
2025-07-01 03:06:04.982 + abcdefGhijkl
2025-07-01 03:06:04.999 """
2025-07-01 03:06:05.014
2025-07-01 03:06:05.026 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:05.044 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:05.054 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:05.063 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:05.076 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:05.088
2025-07-01 03:06:05.104 # search for the pair that matches best without being identical
2025-07-01 03:06:05.116 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:05.130 # on junk -- unless we have to)
2025-07-01 03:06:05.137 for j in range(blo, bhi):
2025-07-01 03:06:05.152 bj = b[j]
2025-07-01 03:06:05.162 cruncher.set_seq2(bj)
2025-07-01 03:06:05.174 for i in range(alo, ahi):
2025-07-01 03:06:05.182 ai = a[i]
2025-07-01 03:06:05.194 if ai == bj:
2025-07-01 03:06:05.202 if eqi is None:
2025-07-01 03:06:05.216 eqi, eqj = i, j
2025-07-01 03:06:05.226 continue
2025-07-01 03:06:05.233 cruncher.set_seq1(ai)
2025-07-01 03:06:05.250 # computing similarity is expensive, so use the quick
2025-07-01 03:06:05.266 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:05.278 # compares by a factor of 3.
2025-07-01 03:06:05.286 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:05.298 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:05.306 # of the computation is cached by cruncher
2025-07-01 03:06:05.322 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:05.330 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:05.342 cruncher.ratio() > best_ratio:
2025-07-01 03:06:05.353 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:05.372 if best_ratio < cutoff:
2025-07-01 03:06:05.380 # no non-identical "pretty close" pair
2025-07-01 03:06:05.396 if eqi is None:
2025-07-01 03:06:05.406 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:05.418 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:05.425 return
2025-07-01 03:06:05.438 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:05.445 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:05.460 else:
2025-07-01 03:06:05.474 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:05.486 eqi = None
2025-07-01 03:06:05.494
2025-07-01 03:06:05.502 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:05.514 # identical
2025-07-01 03:06:05.522
2025-07-01 03:06:05.537 # pump out diffs from before the synch point
2025-07-01 03:06:05.546 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:05.558
2025-07-01 03:06:05.566 # do intraline marking on the synch pair
2025-07-01 03:06:05.578 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:05.585 if eqi is None:
2025-07-01 03:06:05.598 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:05.610 atags = btags = ""
2025-07-01 03:06:05.622 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:05.634 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:05.646 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:05.653 if tag == 'replace':
2025-07-01 03:06:05.661 atags += '^' * la
2025-07-01 03:06:05.678 btags += '^' * lb
2025-07-01 03:06:05.685 elif tag == 'delete':
2025-07-01 03:06:05.701 atags += '-' * la
2025-07-01 03:06:05.710 elif tag == 'insert':
2025-07-01 03:06:05.721 btags += '+' * lb
2025-07-01 03:06:05.732 elif tag == 'equal':
2025-07-01 03:06:05.749 atags += ' ' * la
2025-07-01 03:06:05.758 btags += ' ' * lb
2025-07-01 03:06:05.765 else:
2025-07-01 03:06:05.782 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:05.795 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:05.810 else:
2025-07-01 03:06:05.822 # the synch pair is identical
2025-07-01 03:06:05.830 yield '  ' + aelt
2025-07-01 03:06:05.837
2025-07-01 03:06:05.849 # pump out diffs from after the synch point
2025-07-01 03:06:05.854 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:05.859
2025-07-01 03:06:05.863 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:05.874 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:05.886
2025-07-01 03:06:05.898 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:05.906 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:05.922 alo = 518, ahi = 1101
2025-07-01 03:06:05.929 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:05.933 blo = 518, bhi = 1101
2025-07-01 03:06:05.937
2025-07-01 03:06:05.945 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:05.961 g = []
2025-07-01 03:06:05.970 if alo < ahi:
2025-07-01 03:06:05.981 if blo < bhi:
2025-07-01 03:06:05.994 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:06.002 else:
2025-07-01 03:06:06.014 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:06.022 elif blo < bhi:
2025-07-01 03:06:06.034 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:06.046
2025-07-01 03:06:06.058 >       yield from g
2025-07-01 03:06:06.066
2025-07-01 03:06:06.082 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:06.090 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:06.102
2025-07-01 03:06:06.118 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:06.134 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:06.146 alo = 518, ahi = 1101
2025-07-01 03:06:06.157 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:06.172 blo = 518, bhi = 1101
2025-07-01 03:06:06.186
2025-07-01 03:06:06.194 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:06.211 r"""
2025-07-01 03:06:06.218 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:06.230 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:06.242 synch point, and intraline difference marking is done on the
2025-07-01 03:06:06.251 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:06.266
2025-07-01 03:06:06.271 Example:
2025-07-01 03:06:06.288
2025-07-01 03:06:06.297 >>> d = Differ()
2025-07-01 03:06:06.309 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:06.317 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:06.330 >>> print(''.join(results), end="")
2025-07-01 03:06:06.342 - abcDefghiJkl
2025-07-01 03:06:06.358 + abcdefGhijkl
2025-07-01 03:06:06.382 """
2025-07-01 03:06:06.390
2025-07-01 03:06:06.402 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:06.414 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:06.426 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:06.438 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:06.450 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:06.457
2025-07-01 03:06:06.470 # search for the pair that matches best without being identical
2025-07-01 03:06:06.486 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:06.496 # on junk -- unless we have to)
2025-07-01 03:06:06.512 for j in range(blo, bhi):
2025-07-01 03:06:06.522 bj = b[j]
2025-07-01 03:06:06.534 cruncher.set_seq2(bj)
2025-07-01 03:06:06.545 for i in range(alo, ahi):
2025-07-01 03:06:06.564 ai = a[i]
2025-07-01 03:06:06.574 if ai == bj:
2025-07-01 03:06:06.589 if eqi is None:
2025-07-01 03:06:06.602 eqi, eqj = i, j
2025-07-01 03:06:06.614 continue
2025-07-01 03:06:06.626 cruncher.set_seq1(ai)
2025-07-01 03:06:06.638 # computing similarity is expensive, so use the quick
2025-07-01 03:06:06.650 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:06.662 # compares by a factor of 3.
2025-07-01 03:06:06.670 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:06.686 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:06.698 # of the computation is cached by cruncher
2025-07-01 03:06:06.714 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:06.725 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:06.738 cruncher.ratio() > best_ratio:
2025-07-01 03:06:06.745 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:06.758 if best_ratio < cutoff:
2025-07-01 03:06:06.763 # no non-identical "pretty close" pair
2025-07-01 03:06:06.774 if eqi is None:
2025-07-01 03:06:06.790 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:06.808 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:06.819 return
2025-07-01 03:06:06.834 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:06.843 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:06.855 else:
2025-07-01 03:06:06.878 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:06.890 eqi = None
2025-07-01 03:06:06.902
2025-07-01 03:06:06.909 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:06.922 # identical
2025-07-01 03:06:06.930
2025-07-01 03:06:06.946 # pump out diffs from before the synch point
2025-07-01 03:06:06.958 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:06.970
2025-07-01 03:06:06.978 # do intraline marking on the synch pair
2025-07-01 03:06:06.990 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:07.004 if eqi is None:
2025-07-01 03:06:07.018 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:07.026 atags = btags = ""
2025-07-01 03:06:07.038 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:07.055 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:07.070 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:07.083 if tag == 'replace':
2025-07-01 03:06:07.098 atags += '^' * la
2025-07-01 03:06:07.110 btags += '^' * lb
2025-07-01 03:06:07.118 elif tag == 'delete':
2025-07-01 03:06:07.126 atags += '-' * la
2025-07-01 03:06:07.138 elif tag == 'insert':
2025-07-01 03:06:07.146 btags += '+' * lb
2025-07-01 03:06:07.158 elif tag == 'equal':
2025-07-01 03:06:07.166 atags += ' ' * la
2025-07-01 03:06:07.178 btags += ' ' * lb
2025-07-01 03:06:07.190 else:
2025-07-01 03:06:07.204 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:07.218 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:07.229 else:
2025-07-01 03:06:07.238 # the synch pair is identical
2025-07-01 03:06:07.250 yield '  ' + aelt
2025-07-01 03:06:07.257
2025-07-01 03:06:07.270 # pump out diffs from after the synch point
2025-07-01 03:06:07.278 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:07.289
2025-07-01 03:06:07.298 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:07.310 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:07.322
2025-07-01 03:06:07.329 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:07.342 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:07.349 alo = 519, ahi = 1101
2025-07-01 03:06:07.361 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:07.374 blo = 519, bhi = 1101
2025-07-01 03:06:07.382
2025-07-01 03:06:07.390 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:07.406 g = []
2025-07-01 03:06:07.422 if alo < ahi:
2025-07-01 03:06:07.434 if blo < bhi:
2025-07-01 03:06:07.442 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:07.454 else:
2025-07-01 03:06:07.469 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:07.482 elif blo < bhi:
2025-07-01 03:06:07.494 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:07.509
2025-07-01 03:06:07.517 >       yield from g
2025-07-01 03:06:07.526
2025-07-01 03:06:07.533 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:07.544 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:07.556
2025-07-01 03:06:07.570 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:07.584 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:07.594 alo = 519, ahi = 1101
2025-07-01 03:06:07.606 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:07.618 blo = 519, bhi = 1101
2025-07-01 03:06:07.626
2025-07-01 03:06:07.639 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:07.650 r"""
2025-07-01 03:06:07.666 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:07.678 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:07.686 synch point, and intraline difference marking is done on the
2025-07-01 03:06:07.702 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:07.710
2025-07-01 03:06:07.726 Example:
2025-07-01 03:06:07.734
2025-07-01 03:06:07.746 >>> d = Differ()
2025-07-01 03:06:07.754 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:07.766 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:07.774 >>> print(''.join(results), end="")
2025-07-01 03:06:07.782 - abcDefghiJkl
2025-07-01 03:06:07.810 + abcdefGhijkl
2025-07-01 03:06:07.834 """
2025-07-01 03:06:07.841
2025-07-01 03:06:07.858 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:07.874 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:07.891 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:07.906 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:07.913 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:07.925
2025-07-01 03:06:07.939 # search for the pair that matches best without being identical
2025-07-01 03:06:07.953 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:07.965 # on junk -- unless we have to)
2025-07-01 03:06:07.973 for j in range(blo, bhi):
2025-07-01 03:06:07.986 bj = b[j]
2025-07-01 03:06:08.002 cruncher.set_seq2(bj)
2025-07-01 03:06:08.014 for i in range(alo, ahi):
2025-07-01 03:06:08.026 ai = a[i]
2025-07-01 03:06:08.041 if ai == bj:
2025-07-01 03:06:08.054 if eqi is None:
2025-07-01 03:06:08.066 eqi, eqj = i, j
2025-07-01 03:06:08.074 continue
2025-07-01 03:06:08.081 cruncher.set_seq1(ai)
2025-07-01 03:06:08.098 # computing similarity is expensive, so use the quick
2025-07-01 03:06:08.105 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:08.117 # compares by a factor of 3.
2025-07-01 03:06:08.126 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:08.138 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:08.146 # of the computation is cached by cruncher
2025-07-01 03:06:08.162 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:08.170 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:08.178 cruncher.ratio() > best_ratio:
2025-07-01 03:06:08.194 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:08.202 if best_ratio < cutoff:
2025-07-01 03:06:08.218 # no non-identical "pretty close" pair
2025-07-01 03:06:08.230 if eqi is None:
2025-07-01 03:06:08.238 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:08.250 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:08.258 return
2025-07-01 03:06:08.270 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:08.278 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:08.288 else:
2025-07-01 03:06:08.302 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:08.314 eqi = None
2025-07-01 03:06:08.322
2025-07-01 03:06:08.329 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:08.341 # identical
2025-07-01 03:06:08.348
2025-07-01 03:06:08.362 # pump out diffs from before the synch point
2025-07-01 03:06:08.374 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:08.382
2025-07-01 03:06:08.392 # do intraline marking on the synch pair
2025-07-01 03:06:08.401 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:08.417 if eqi is None:
2025-07-01 03:06:08.430 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:08.438 atags = btags = ""
2025-07-01 03:06:08.445 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:08.457 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:08.465 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:08.482 if tag == 'replace':
2025-07-01 03:06:08.494 atags += '^' * la
2025-07-01 03:06:08.510 btags += '^' * lb
2025-07-01 03:06:08.522 elif tag == 'delete':
2025-07-01 03:06:08.534 atags += '-' * la
2025-07-01 03:06:08.542 elif tag == 'insert':
2025-07-01 03:06:08.556 btags += '+' * lb
2025-07-01 03:06:08.570 elif tag == 'equal':
2025-07-01 03:06:08.586 atags += ' ' * la
2025-07-01 03:06:08.598 btags += ' ' * lb
2025-07-01 03:06:08.605 else:
2025-07-01 03:06:08.618 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:08.634 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:08.645 else:
2025-07-01 03:06:08.659 # the synch pair is identical
2025-07-01 03:06:08.670 yield '  ' + aelt
2025-07-01 03:06:08.678
2025-07-01 03:06:08.692 # pump out diffs from after the synch point
2025-07-01 03:06:08.706 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:08.713
2025-07-01 03:06:08.722 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:08.734 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:08.746
2025-07-01 03:06:08.762 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:08.774 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:08.782 alo = 520, ahi = 1101
2025-07-01 03:06:08.798 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:08.810 blo = 520, bhi = 1101
2025-07-01 03:06:08.818
2025-07-01 03:06:08.827 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:08.834 g = []
2025-07-01 03:06:08.843 if alo < ahi:
2025-07-01 03:06:08.854 if blo < bhi:
2025-07-01 03:06:08.860 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:08.873 else:
2025-07-01 03:06:08.882 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:08.898 elif blo < bhi:
2025-07-01 03:06:08.910 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:08.918
2025-07-01 03:06:08.934 >       yield from g
2025-07-01 03:06:08.944
2025-07-01 03:06:08.960 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:08.974 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:08.991
2025-07-01 03:06:09.006 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:09.026 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:09.046 alo = 520, ahi = 1101
2025-07-01 03:06:09.059 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:09.069 blo = 520, bhi = 1101
2025-07-01 03:06:09.082
2025-07-01 03:06:09.094 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:09.110 r"""
2025-07-01 03:06:09.121 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:09.129 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:09.137 synch point, and intraline difference marking is done on the
2025-07-01 03:06:09.148 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:09.162
2025-07-01 03:06:09.174 Example:
2025-07-01 03:06:09.180
2025-07-01 03:06:09.192 >>> d = Differ()
2025-07-01 03:06:09.210 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:09.222 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:09.234 >>> print(''.join(results), end="")
2025-07-01 03:06:09.246 - abcDefghiJkl
2025-07-01 03:06:09.262 + abcdefGhijkl
2025-07-01 03:06:09.282 """
2025-07-01 03:06:09.298
2025-07-01 03:06:09.310 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:09.326 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:09.336 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:09.346 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:09.354 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:09.366
2025-07-01 03:06:09.372 # search for the pair that matches best without being identical
2025-07-01 03:06:09.388 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:09.402 # on junk -- unless we have to)
2025-07-01 03:06:09.418 for j in range(blo, bhi):
2025-07-01 03:06:09.433 bj = b[j]
2025-07-01 03:06:09.442 cruncher.set_seq2(bj)
2025-07-01 03:06:09.453 for i in range(alo, ahi):
2025-07-01 03:06:09.465 ai = a[i]
2025-07-01 03:06:09.477 if ai == bj:
2025-07-01 03:06:09.490 if eqi is None:
2025-07-01 03:06:09.498 eqi, eqj = i, j
2025-07-01 03:06:09.510 continue
2025-07-01 03:06:09.522 cruncher.set_seq1(ai)
2025-07-01 03:06:09.534 # computing similarity is expensive, so use the quick
2025-07-01 03:06:09.541 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:09.553 # compares by a factor of 3.
2025-07-01 03:06:09.561 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:09.570 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:09.586 # of the computation is cached by cruncher
2025-07-01 03:06:09.594 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:09.606 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:09.613 cruncher.ratio() > best_ratio:
2025-07-01 03:06:09.626 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:09.638 if best_ratio < cutoff:
2025-07-01 03:06:09.650 # no non-identical "pretty close" pair
2025-07-01 03:06:09.661 if eqi is None:
2025-07-01 03:06:09.674 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:09.688 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:09.698 return
2025-07-01 03:06:09.714 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:09.723 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:09.734 else:
2025-07-01 03:06:09.746 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:09.757 eqi = None
2025-07-01 03:06:09.769
2025-07-01 03:06:09.777 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:09.782 # identical
2025-07-01 03:06:09.786
2025-07-01 03:06:09.797 # pump out diffs from before the synch point
2025-07-01 03:06:09.809 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:09.818
2025-07-01 03:06:09.825 # do intraline marking on the synch pair
2025-07-01 03:06:09.834 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:09.842 if eqi is None:
2025-07-01 03:06:09.858 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:09.870 atags = btags = ""
2025-07-01 03:06:09.877 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:09.889 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:09.902 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:09.914 if tag == 'replace':
2025-07-01 03:06:09.929 atags += '^' * la
2025-07-01 03:06:09.938 btags += '^' * lb
2025-07-01 03:06:09.950 elif tag == 'delete':
2025-07-01 03:06:09.962 atags += '-' * la
2025-07-01 03:06:09.974 elif tag == 'insert':
2025-07-01 03:06:09.986 btags += '+' * lb
2025-07-01 03:06:10.002 elif tag == 'equal':
2025-07-01 03:06:10.010 atags += ' ' * la
2025-07-01 03:06:10.018 btags += ' ' * lb
2025-07-01 03:06:10.031 else:
2025-07-01 03:06:10.038 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:10.053 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:10.062 else:
2025-07-01 03:06:10.074 # the synch pair is identical
2025-07-01 03:06:10.083 yield '  ' + aelt
2025-07-01 03:06:10.094
2025-07-01 03:06:10.105 # pump out diffs from after the synch point
2025-07-01 03:06:10.116 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:10.126
2025-07-01 03:06:10.137 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:10.148 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:10.162
2025-07-01 03:06:10.174 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:10.186 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:10.194 alo = 521, ahi = 1101
2025-07-01 03:06:10.208 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:10.218 blo = 521, bhi = 1101
2025-07-01 03:06:10.226
2025-07-01 03:06:10.238 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:10.254 g = []
2025-07-01 03:06:10.261 if alo < ahi:
2025-07-01 03:06:10.274 if blo < bhi:
2025-07-01 03:06:10.286 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:10.294 else:
2025-07-01 03:06:10.302 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:10.314 elif blo < bhi:
2025-07-01 03:06:10.322 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:10.334
2025-07-01 03:06:10.342 >       yield from g
2025-07-01 03:06:10.352
2025-07-01 03:06:10.364 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:10.376 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:10.390
2025-07-01 03:06:10.395 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:10.409 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:10.417 alo = 521, ahi = 1101
2025-07-01 03:06:10.428 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:10.439 blo = 521, bhi = 1101
2025-07-01 03:06:10.458
2025-07-01 03:06:10.466 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:10.478 r"""
2025-07-01 03:06:10.490 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:10.498 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:10.510 synch point, and intraline difference marking is done on the
2025-07-01 03:06:10.518 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:10.530
2025-07-01 03:06:10.538 Example:
2025-07-01 03:06:10.550
2025-07-01 03:06:10.558 >>> d = Differ()
2025-07-01 03:06:10.570 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:10.578 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:10.590 >>> print(''.join(results), end="")
2025-07-01 03:06:10.598 - abcDefghiJkl
2025-07-01 03:06:10.625 + abcdefGhijkl
2025-07-01 03:06:10.642 """
2025-07-01 03:06:10.655
2025-07-01 03:06:10.665 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:10.682 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:10.690 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:10.702 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:10.714 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:10.722
2025-07-01 03:06:10.734 # search for the pair that matches best without being identical
2025-07-01 03:06:10.742 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:10.758 # on junk -- unless we have to)
2025-07-01 03:06:10.768 for j in range(blo, bhi):
2025-07-01 03:06:10.781 bj = b[j]
2025-07-01 03:06:10.794 cruncher.set_seq2(bj)
2025-07-01 03:06:10.801 for i in range(alo, ahi):
2025-07-01 03:06:10.815 ai = a[i]
2025-07-01 03:06:10.822 if ai == bj:
2025-07-01 03:06:10.833 if eqi is None:
2025-07-01 03:06:10.842 eqi, eqj = i, j
2025-07-01 03:06:10.850 continue
2025-07-01 03:06:10.864 cruncher.set_seq1(ai)
2025-07-01 03:06:10.874 # computing similarity is expensive, so use the quick
2025-07-01 03:06:10.886 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:10.898 # compares by a factor of 3.
2025-07-01 03:06:10.910 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:10.918 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:10.930 # of the computation is cached by cruncher
2025-07-01 03:06:10.938 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:10.952 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:10.962 cruncher.ratio() > best_ratio:
2025-07-01 03:06:10.982 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:10.998 if best_ratio < cutoff:
2025-07-01 03:06:11.005 # no non-identical "pretty close" pair
2025-07-01 03:06:11.021 if eqi is None:
2025-07-01 03:06:11.032 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:11.046 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:11.053 return
2025-07-01 03:06:11.069 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:11.076 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:11.085 else:
2025-07-01 03:06:11.094 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:11.103 eqi = None
2025-07-01 03:06:11.110
2025-07-01 03:06:11.116 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:11.134 # identical
2025-07-01 03:06:11.142
2025-07-01 03:06:11.154 # pump out diffs from before the synch point
2025-07-01 03:06:11.162 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:11.174
2025-07-01 03:06:11.186 # do intraline marking on the synch pair
2025-07-01 03:06:11.194 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:11.203 if eqi is None:
2025-07-01 03:06:11.213 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:11.222 atags = btags = ""
2025-07-01 03:06:11.234 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:11.241 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:11.254 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:11.261 if tag == 'replace':
2025-07-01 03:06:11.274 atags += '^' * la
2025-07-01 03:06:11.285 btags += '^' * lb
2025-07-01 03:06:11.298 elif tag == 'delete':
2025-07-01 03:06:11.305 atags += '-' * la
2025-07-01 03:06:11.318 elif tag == 'insert':
2025-07-01 03:06:11.326 btags += '+' * lb
2025-07-01 03:06:11.336 elif tag == 'equal':
2025-07-01 03:06:11.350 atags += ' ' * la
2025-07-01 03:06:11.366 btags += ' ' * lb
2025-07-01 03:06:11.377 else:
2025-07-01 03:06:11.394 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:11.402 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:11.418 else:
2025-07-01 03:06:11.426 # the synch pair is identical
2025-07-01 03:06:11.438 yield '  ' + aelt
2025-07-01 03:06:11.445
2025-07-01 03:06:11.458 # pump out diffs from after the synch point
2025-07-01 03:06:11.466 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:11.474
2025-07-01 03:06:11.486 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:11.494 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:11.506
2025-07-01 03:06:11.514 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:11.526 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:11.538 alo = 522, ahi = 1101
2025-07-01 03:06:11.546 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:11.562 blo = 522, bhi = 1101
2025-07-01 03:06:11.568
2025-07-01 03:06:11.581 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:11.592 g = []
2025-07-01 03:06:11.606 if alo < ahi:
2025-07-01 03:06:11.622 if blo < bhi:
2025-07-01 03:06:11.629 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:11.638 else:
2025-07-01 03:06:11.651 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:11.661 elif blo < bhi:
2025-07-01 03:06:11.666 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:11.678
2025-07-01 03:06:11.685 >       yield from g
2025-07-01 03:06:11.698
2025-07-01 03:06:11.710 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:11.722 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:11.734
2025-07-01 03:06:11.746 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:11.757 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:11.766 alo = 522, ahi = 1101
2025-07-01 03:06:11.786 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:11.798 blo = 522, bhi = 1101
2025-07-01 03:06:11.810
2025-07-01 03:06:11.820 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:11.832 r"""
2025-07-01 03:06:11.847 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:11.856 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:11.869 synch point, and intraline difference marking is done on the
2025-07-01 03:06:11.886 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:11.901
2025-07-01 03:06:11.910 Example:
2025-07-01 03:06:11.927
2025-07-01 03:06:11.942 >>> d = Differ()
2025-07-01 03:06:11.958 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:11.969 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:11.982 >>> print(''.join(results), end="")
2025-07-01 03:06:11.994 - abcDefghiJkl
2025-07-01 03:06:12.018 + abcdefGhijkl
2025-07-01 03:06:12.034 """
2025-07-01 03:06:12.040
2025-07-01 03:06:12.047 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:12.052 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:12.056 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:12.076 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:12.085 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:12.098
2025-07-01 03:06:12.106 # search for the pair that matches best without being identical
2025-07-01 03:06:12.124 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:12.132 # on junk -- unless we have to)
2025-07-01 03:06:12.149 for j in range(blo, bhi):
2025-07-01 03:06:12.154 bj = b[j]
2025-07-01 03:06:12.158 cruncher.set_seq2(bj)
2025-07-01 03:06:12.163 for i in range(alo, ahi):
2025-07-01 03:06:12.168 ai = a[i]
2025-07-01 03:06:12.172 if ai == bj:
2025-07-01 03:06:12.177 if eqi is None:
2025-07-01 03:06:12.181 eqi, eqj = i, j
2025-07-01 03:06:12.186 continue
2025-07-01 03:06:12.190 cruncher.set_seq1(ai)
2025-07-01 03:06:12.195 # computing similarity is expensive, so use the quick
2025-07-01 03:06:12.200 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:12.205 # compares by a factor of 3.
2025-07-01 03:06:12.210 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:12.214 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:12.219 # of the computation is cached by cruncher
2025-07-01 03:06:12.223 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:12.229 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:12.233 cruncher.ratio() > best_ratio:
2025-07-01 03:06:12.238 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:12.242 if best_ratio < cutoff:
2025-07-01 03:06:12.246 # no non-identical "pretty close" pair
2025-07-01 03:06:12.251 if eqi is None:
2025-07-01 03:06:12.255 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:12.266 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:12.278 return
2025-07-01 03:06:12.286 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:12.302 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:12.310 else:
2025-07-01 03:06:12.322 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:12.330 eqi = None
2025-07-01 03:06:12.342
2025-07-01 03:06:12.354 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:12.362 # identical
2025-07-01 03:06:12.374
2025-07-01 03:06:12.390 # pump out diffs from before the synch point
2025-07-01 03:06:12.398 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:12.414
2025-07-01 03:06:12.422 # do intraline marking on the synch pair
2025-07-01 03:06:12.438 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:12.450 if eqi is None:
2025-07-01 03:06:12.462 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:12.472 atags = btags = ""
2025-07-01 03:06:12.482 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:12.498 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:12.510 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:12.522 if tag == 'replace':
2025-07-01 03:06:12.537 atags += '^' * la
2025-07-01 03:06:12.551 btags += '^' * lb
2025-07-01 03:06:12.562 elif tag == 'delete':
2025-07-01 03:06:12.571 atags += '-' * la
2025-07-01 03:06:12.580 elif tag == 'insert':
2025-07-01 03:06:12.589 btags += '+' * lb
2025-07-01 03:06:12.602 elif tag == 'equal':
2025-07-01 03:06:12.612 atags += ' ' * la
2025-07-01 03:06:12.620 btags += ' ' * lb
2025-07-01 03:06:12.637 else:
2025-07-01 03:06:12.649 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:12.662 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:12.670 else:
2025-07-01 03:06:12.678 # the synch pair is identical
2025-07-01 03:06:12.688 yield '  ' + aelt
2025-07-01 03:06:12.700
2025-07-01 03:06:12.718 # pump out diffs from after the synch point
2025-07-01 03:06:12.728 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:12.737
2025-07-01 03:06:12.750 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:12.757 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:12.768
2025-07-01 03:06:12.778 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:12.790 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:12.806 alo = 523, ahi = 1101
2025-07-01 03:06:12.819 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:12.835 blo = 523, bhi = 1101
2025-07-01 03:06:12.845
2025-07-01 03:06:12.856 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:12.867 g = []
2025-07-01 03:06:12.884 if alo < ahi:
2025-07-01 03:06:12.890 if blo < bhi:
2025-07-01 03:06:12.906 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:12.911 else:
2025-07-01 03:06:12.924 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:12.937 elif blo < bhi:
2025-07-01 03:06:12.950 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:12.958
2025-07-01 03:06:12.973 >       yield from g
2025-07-01 03:06:12.981
2025-07-01 03:06:12.997 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:13.010 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:13.022
2025-07-01 03:06:13.033 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:13.040 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:13.046 alo = 523, ahi = 1101
2025-07-01 03:06:13.052 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:13.058 blo = 523, bhi = 1101
2025-07-01 03:06:13.064
2025-07-01 03:06:13.073 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:13.077 r"""
2025-07-01 03:06:13.082 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:13.086 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:13.091 synch point, and intraline difference marking is done on the
2025-07-01 03:06:13.109 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:13.114
2025-07-01 03:06:13.126 Example:
2025-07-01 03:06:13.138
2025-07-01 03:06:13.149 >>> d = Differ()
2025-07-01 03:06:13.153 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:13.165 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:13.177 >>> print(''.join(results), end="")
2025-07-01 03:06:13.186 - abcDefghiJkl
2025-07-01 03:06:13.210 + abcdefGhijkl
2025-07-01 03:06:13.234 """
2025-07-01 03:06:13.246
2025-07-01 03:06:13.254 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:13.266 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:13.274 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:13.287 cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:13.297 eqi, eqj = None, None   # 1st indices of equal lines (if any)
2025-07-01 03:06:13.310
2025-07-01 03:06:13.318 # search for the pair that matches best without being identical
2025-07-01 03:06:13.328 # (identical lines must be junk lines, & we don't want to synch up
2025-07-01 03:06:13.342 # on junk -- unless we have to)
2025-07-01 03:06:13.350 for j in range(blo, bhi):
2025-07-01 03:06:13.359 bj = b[j]
2025-07-01 03:06:13.369 cruncher.set_seq2(bj)
2025-07-01 03:06:13.382 for i in range(alo, ahi):
2025-07-01 03:06:13.398 ai = a[i]
2025-07-01 03:06:13.405 if ai == bj:
2025-07-01 03:06:13.416 if eqi is None:
2025-07-01 03:06:13.430 eqi, eqj = i, j
2025-07-01 03:06:13.446 continue
2025-07-01 03:06:13.461 cruncher.set_seq1(ai)
2025-07-01 03:06:13.469 # computing similarity is expensive, so use the quick
2025-07-01 03:06:13.474 # upper bounds first -- have seen this speed up messy
2025-07-01 03:06:13.479 # compares by a factor of 3.
2025-07-01 03:06:13.484 # note that ratio() is only expensive to compute the first
2025-07-01 03:06:13.489 # time it's called on a sequence pair; the expensive part
2025-07-01 03:06:13.494 # of the computation is cached by cruncher
2025-07-01 03:06:13.499 if cruncher.real_quick_ratio() > best_ratio and \
2025-07-01 03:06:13.503 cruncher.quick_ratio() > best_ratio and \
2025-07-01 03:06:13.509 cruncher.ratio() > best_ratio:
2025-07-01 03:06:13.514 best_ratio, best_i, best_j = cruncher.ratio(), i, j
2025-07-01 03:06:13.519 if best_ratio < cutoff:
2025-07-01 03:06:13.524 # no non-identical "pretty close" pair
2025-07-01 03:06:13.529 if eqi is None:
2025-07-01 03:06:13.534 # no identical pair either -- treat it as a straight replace
2025-07-01 03:06:13.539 yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:13.543 return
2025-07-01 03:06:13.548 # no close pair, but an identical pair -- synch up on that
2025-07-01 03:06:13.553 best_i, best_j, best_ratio = eqi, eqj, 1.0
2025-07-01 03:06:13.561 else:
2025-07-01 03:06:13.566 # there's a close pair, so forget the identical pair (if any)
2025-07-01 03:06:13.573 eqi = None
2025-07-01 03:06:13.582
2025-07-01 03:06:13.594 # a[best_i] very similar to b[best_j]; eqi is None iff they're not
2025-07-01 03:06:13.610 # identical
2025-07-01 03:06:13.621
2025-07-01 03:06:13.633 # pump out diffs from before the synch point
2025-07-01 03:06:13.642 yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
2025-07-01 03:06:13.651
2025-07-01 03:06:13.656 # do intraline marking on the synch pair
2025-07-01 03:06:13.666 aelt, belt = a[best_i], b[best_j]
2025-07-01 03:06:13.673 if eqi is None:
2025-07-01 03:06:13.678 # pump out a '-', '?', '+', '?' quad for the synched lines
2025-07-01 03:06:13.684 atags = btags = ""
2025-07-01 03:06:13.689 cruncher.set_seqs(aelt, belt)
2025-07-01 03:06:13.694 for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes():
2025-07-01 03:06:13.698 la, lb = ai2 - ai1, bj2 - bj1
2025-07-01 03:06:13.703 if tag == 'replace':
2025-07-01 03:06:13.708 atags += '^' * la
2025-07-01 03:06:13.713 btags += '^' * lb
2025-07-01 03:06:13.718 elif tag == 'delete':
2025-07-01 03:06:13.723 atags += '-' * la
2025-07-01 03:06:13.728 elif tag == 'insert':
2025-07-01 03:06:13.732 btags += '+' * lb
2025-07-01 03:06:13.737 elif tag == 'equal':
2025-07-01 03:06:13.741 atags += ' ' * la
2025-07-01 03:06:13.746 btags += ' ' * lb
2025-07-01 03:06:13.751 else:
2025-07-01 03:06:13.755 raise ValueError('unknown tag %r' % (tag,))
2025-07-01 03:06:13.760 yield from self._qformat(aelt, belt, atags, btags)
2025-07-01 03:06:13.764 else:
2025-07-01 03:06:13.769 # the synch pair is identical
2025-07-01 03:06:13.773 yield '  ' + aelt
2025-07-01 03:06:13.778
2025-07-01 03:06:13.782 # pump out diffs from after the synch point
2025-07-01 03:06:13.787 >       yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
2025-07-01 03:06:13.792
2025-07-01 03:06:13.796 /usr/lib/python3.11/difflib.py:985:
2025-07-01 03:06:13.801 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:13.806
2025-07-01 03:06:13.810 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:13.816 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:13.821 alo = 524, ahi = 1101
2025-07-01 03:06:13.827 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:13.832 blo = 524, bhi = 1101
2025-07-01 03:06:13.836
2025-07-01 03:06:13.841 def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:13.845 g = []
2025-07-01 03:06:13.849 if alo < ahi:
2025-07-01 03:06:13.853 if blo < bhi:
2025-07-01 03:06:13.858 g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
2025-07-01 03:06:13.862 else:
2025-07-01 03:06:13.867 g = self._dump('-', a, alo, ahi)
2025-07-01 03:06:13.871 elif blo < bhi:
2025-07-01 03:06:13.875 g = self._dump('+', b, blo, bhi)
2025-07-01 03:06:13.880
2025-07-01 03:06:13.884 >       yield from g
2025-07-01 03:06:13.889
2025-07-01 03:06:13.893 /usr/lib/python3.11/difflib.py:997:
2025-07-01 03:06:13.898 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:13.902
2025-07-01 03:06:13.906 self = <difflib.Differ object at [hex]>
2025-07-01 03:06:13.911 a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
2025-07-01 03:06:13.915 alo = 524, ahi = 1101
2025-07-01 03:06:13.920 b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
2025-07-01 03:06:13.924 blo = 524, bhi = 1101
2025-07-01 03:06:13.929
2025-07-01 03:06:13.933 def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
2025-07-01 03:06:13.937 r"""
2025-07-01 03:06:13.942 When replacing one block of lines with another, search the blocks
2025-07-01 03:06:13.946 for *similar* lines; the best-matching pair (if any) is used as a
2025-07-01 03:06:13.950 synch point, and intraline difference marking is done on the
2025-07-01 03:06:13.955 similar pair. Lots of work, but often worth it.
2025-07-01 03:06:13.959
2025-07-01 03:06:13.963 Example:
2025-07-01 03:06:13.968
2025-07-01 03:06:13.972 >>> d = Differ()
2025-07-01 03:06:13.976 >>> results = d._fancy_replace(['abcDefghiJkl\n'], 0, 1,
2025-07-01 03:06:13.984 ...                            ['abcdefGhijkl\n'], 0, 1)
2025-07-01 03:06:13.989 >>> print(''.join(results), end="")
2025-07-01 03:06:13.994 - abcDefghiJkl
2025-07-01 03:06:14.004 + abcdefGhijkl
2025-07-01 03:06:14.012 """
2025-07-01 03:06:14.016
2025-07-01 03:06:14.022 # don't synch up unless the lines have a similarity score of at
2025-07-01 03:06:14.027 # least cutoff; best_ratio tracks the best score seen so far
2025-07-01 03:06:14.033 best_ratio, cutoff = 0.74, 0.75
2025-07-01 03:06:14.037 >       cruncher = SequenceMatcher(self.charjunk)
2025-07-01 03:06:14.042
2025-07-01 03:06:14.047 /usr/lib/python3.11/difflib.py:915:
2025-07-01 03:06:14.052 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:14.059
2025-07-01 03:06:14.075 self = <difflib.SequenceMatcher object at [hex]>
2025-07-01 03:06:14.090 isjunk = <function IS_CHARACTER_JUNK at 0x7fee4cf1b4c0>, a = '', b = ''
2025-07-01 03:06:14.104 autojunk = True
2025-07-01 03:06:14.114
2025-07-01 03:06:14.126 def __init__(self, isjunk=None, a='', b='', autojunk=True):
2025-07-01 03:06:14.137 """Construct a SequenceMatcher.
2025-07-01 03:06:14.150
2025-07-01 03:06:14.170 Optional arg isjunk is None (the default), or a one-argument
2025-07-01 03:06:14.187 function that takes a sequence element and returns true iff the
2025-07-01 03:06:14.198 element is junk.  None is equivalent to passing "lambda x: 0", i.e.
2025-07-01 03:06:14.214 no elements are considered to be junk.  For example, pass
2025-07-01 03:06:14.226 lambda x: x in " \\t"
2025-07-01 03:06:14.241 if you're comparing lines as sequences of characters, and don't
2025-07-01 03:06:14.258 want to synch up on blanks or hard tabs.
2025-07-01 03:06:14.266
2025-07-01 03:06:14.278 Optional arg a is the first of two sequences to be compared.  By
2025-07-01 03:06:14.286 default, an empty string.  The elements of a must be hashable.  See
2025-07-01 03:06:14.298 also .set_seqs() and .set_seq1().
2025-07-01 03:06:14.310
2025-07-01 03:06:14.322 Optional arg b is the second of two sequences to be compared.  By
2025-07-01 03:06:14.330 default, an empty string.  The elements of b must be hashable. See
2025-07-01 03:06:14.342 also .set_seqs() and .set_seq2().
2025-07-01 03:06:14.350
2025-07-01 03:06:14.360 Optional arg autojunk should be set to False to disable the
2025-07-01 03:06:14.377 "automatic junk heuristic" that treats popular elements as junk
2025-07-01 03:06:14.390 (see module documentation for more information).
2025-07-01 03:06:14.406 """
2025-07-01 03:06:14.422
2025-07-01 03:06:14.434 # Members:
2025-07-01 03:06:14.442 # a
2025-07-01 03:06:14.453 #      first sequence
2025-07-01 03:06:14.465 # b
2025-07-01 03:06:14.472 #      second sequence; differences are computed as "what do
2025-07-01 03:06:14.488 #      we need to do to 'a' to change it into 'b'?"
2025-07-01 03:06:14.496 # b2j
2025-07-01 03:06:14.504 #      for x in b, b2j[x] is a list of the indices (into b)
2025-07-01 03:06:14.518 #      at which x appears; junk and popular elements do not appear
2025-07-01 03:06:14.526 # fullbcount
2025-07-01 03:06:14.537 #      for x in b, fullbcount[x] == the number of times x
2025-07-01 03:06:14.545 #      appears in b; only materialized if really needed (used
2025-07-01 03:06:14.562 #      only for computing quick_ratio())
2025-07-01 03:06:14.570 # matching_blocks
2025-07-01 03:06:14.582 #      a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k];
2025-07-01 03:06:14.590 #      ascending & non-overlapping in i and in j; terminated by
2025-07-01 03:06:14.602 #      a dummy (len(a), len(b), 0) sentinel
2025-07-01 03:06:14.610 # opcodes
2025-07-01 03:06:14.625 #      a list of (tag, i1, i2, j1, j2) tuples, where tag is
2025-07-01 03:06:14.638 #      one of
2025-07-01 03:06:14.646 #          'replace'   a[i1:i2] should be replaced by b[j1:j2]
2025-07-01 03:06:14.658 #          'delete'    a[i1:i2] should be deleted
2025-07-01 03:06:14.666 #          'insert'    b[j1:j2] should be inserted
2025-07-01 03:06:14.673 #          'equal'     a[i1:i2] == b[j1:j2]
2025-07-01 03:06:14.684 # isjunk
2025-07-01 03:06:14.690 #      a user-supplied function taking a sequence element and
2025-07-01 03:06:14.695 #      returning true iff the element is "junk" -- this has
2025-07-01 03:06:14.701 #      subtle but helpful effects on the algorithm, which I'll
2025-07-01 03:06:14.706 #      get around to writing up someday <0.9 wink>.
2025-07-01 03:06:14.713 #      DON'T USE!  Only __chain_b uses this.  Use "in self.bjunk".
2025-07-01 03:06:14.717 # bjunk
2025-07-01 03:06:14.730 #      the items in b for which isjunk is True.
2025-07-01 03:06:14.740 # bpopular
2025-07-01 03:06:14.753 #      nonjunk items in b treated as junk by the heuristic (if used).
2025-07-01 03:06:14.761
2025-07-01 03:06:14.773 self.isjunk = isjunk
2025-07-01 03:06:14.781 self.a = self.b = None
2025-07-01 03:06:14.794 self.autojunk = autojunk
2025-07-01 03:06:14.806 >       self.set_seqs(a, b)
2025-07-01 03:06:14.814
2025-07-01 03:06:14.826 /usr/lib/python3.11/difflib.py:182:
2025-07-01 03:06:14.850 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:14.858
2025-07-01 03:06:14.865 self = <difflib.SequenceMatcher object at [hex]>, a = '', b = ''
2025-07-01 03:06:14.882
2025-07-01 03:06:14.890 def set_seqs(self, a, b):
2025-07-01 03:06:14.897 """Set the two sequences to be compared.
2025-07-01 03:06:14.910
2025-07-01 03:06:14.918 >>> s = SequenceMatcher()
2025-07-01 03:06:14.930 >>> s.set_seqs("abcd", "bcde")
2025-07-01 03:06:14.942 >>> s.ratio()
2025-07-01 03:06:14.950 0.75
2025-07-01 03:06:14.957 """
2025-07-01 03:06:14.970
2025-07-01 03:06:14.980 self.set_seq1(a)
2025-07-01 03:06:14.994 >       self.set_seq2(b)
2025-07-01 03:06:15.006
2025-07-01 03:06:15.018 /usr/lib/python3.11/difflib.py:194:
2025-07-01 03:06:15.028 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2025-07-01 03:06:15.041
2025-07-01 03:06:15.053 self = <difflib.SequenceMatcher object at [hex]>, b = ''
2025-07-01 03:06:15.071
2025-07-01 03:06:15.084 def set_seq2(self, b):
2025-07-01 03:06:15.095 """Set the second sequence to be compared.
2025-07-01 03:06:15.099
2025-07-01 03:06:15.104 The first sequence to be compared is not changed.
2025-07-01 03:06:15.112
2025-07-01 03:06:15.117 >>> s = SequenceMatcher(None, "abcd", "bcde")
2025-07-01 03:06:15.121 >>> s.ratio()
2025-07-01 03:06:15.126 0.75
2025-07-01 03:06:15.131 >>> s.set_seq2("abcd")
2025-07-01 03:06:15.135 >>> s.ratio()
2025-07-01 03:06:15.140 1.0
2025-07-01 03:06:15.144 >>>
2025-07-01 03:06:15.149
2025-07-01 03:06:15.154 SequenceMatcher computes and caches detailed information about the
2025-07-01 03:06:15.158 second sequence, so if you want to compare one sequence S against
2025-07-01 03:06:15.163 many sequences, use .set_seq2(S) once and call .set_seq1(x)
2025-07-01 03:06:15.167 repeatedly for each of the other sequences.
2025-07-01 03:06:15.172
2025-07-01 03:06:15.176 See also set_seqs() and set_seq1().
2025-07-01 03:06:15.181 """
2025-07-01 03:06:15.185
2025-07-01 03:06:15.189 if b is self.b:
2025-07-01 03:06:15.194 return
2025-07-01 03:06:15.199 self.b = b
2025-07-01 03:06:15.203 self.matching_blocks = self.opcodes = None
2025-07-01 03:06:15.208 self.fullbcount = None
2025-07-01 03:06:15.213 >       self.__chain_b()
2025-07-01 03:06:15.217 E       RecursionError: maximum recursion depth exceeded
2025-07-01 03:06:15.222
2025-07-01 03:06:15.226 /usr/lib/python3.11/difflib.py:248: RecursionError
2025-07-01 03:06:15.231 ---------------------------- Captured stdout setup -----------------------------
2025-07-01 03:06:15.236 Creating db: localhost:/var/tmp/qa_2024/test_1507/test.fdb [page_size=None, sql_dialect=None, charset='NONE', user=SYSDBA, password=masterkey]
3 #text
act = <firebird.qa.plugin.Action pytest object at [hex]>

    @pytest.mark.version('>=3')
    def test_1(act: Action):
        act.expected_stdout = expected_stdout
        act.execute()
>       assert act.clean_stdout == act.clean_expected_stdout

tests/bugs/core_2969_test.py:1211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ops = ('==',), results = (False,)
expls = ('%(py2)s\n{%(py2)s = %(py0)s.clean_stdout\n} == %(py6)s\n{%(py6)s = %(py4)s.clean_expected_stdout\n}',)
each_obj = ('WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var...yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy')

    def _call_reprcompare(
        ops: Sequence[str],
        results: Sequence[bool],
        expls: Sequence[str],
        each_obj: Sequence[object],
    ) -> str:
        for i, res, expl in zip(range(len(ops)), results, expls):
            try:
                done = not res
            except Exception:
                done = True
            if done:
                break
        if util._reprcompare is not None:
>           custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1])

../lib/python3.11/site-packages/_pytest/assertion/rewrite.py:499: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

op = '=='
left = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var_...    yyy\n1var_997                        yyy\n1var_998                        yyy\n1var_999                        yyy'
right = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1 var... yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'

    def callbinrepr(op, left: object, right: object) -> Optional[str]:
        """Call the pytest_assertrepr_compare hook and prepare the result.
    
        This uses the first result from the hook and then ensures the
        following:
        * Overly verbose explanations are truncated unless configured otherwise
          (eg. if running in verbose mode).
        * Embedded newlines are escaped to help util.format_explanation()
          later.
        * If the rewrite mode is used embedded %-characters are replaced
          to protect later % formatting.
    
        The result can be formatted by util.format_explanation() for
        pretty printing.
        """
>       hook_result = ihook.pytest_assertrepr_compare(
            config=item.config, op=op, left=left, right=right
        )

../lib/python3.11/site-packages/_pytest/assertion/__init__.py:141: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <HookCaller 'pytest_assertrepr_compare'>
kwargs = {'config': <_pytest.config.Config pytest object at [hex]>, 'left': 'WAS_OVERWRITTEN CTX_KEY                        C...yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'}
firstresult = False

    def __call__(self, **kwargs: object) -> Any:
        """Call the hook.
    
        Only accepts keyword arguments, which should match the hook
        specification.
    
        Returns the result(s) of calling all registered plugins, see
        :ref:`calling`.
        """
        assert (
            not self.is_historic()
        ), "Cannot directly call a historic hook - use call_historic instead."
        self._verify_all_args_are_provided(kwargs)
        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
>       return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)

../lib/python3.11/site-packages/pluggy/_hooks.py:493: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.config.PytestPluginManager pytest object at [hex]>
hook_name = 'pytest_assertrepr_compare'
methods = [<HookImpl plugin_name='assertion', plugin=<module '_pytest.assertion' from '/opt/distr/venv/lib/python3.11/site-packa...in_name='firebird', plugin=<module 'firebird.qa.plugin' from '/opt/distr/venv/firebird-qa/src/firebird/qa/plugin.py'>>]
kwargs = {'config': <_pytest.config.Config pytest object at [hex]>, 'left': 'WAS_OVERWRITTEN CTX_KEY                        C...yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'}
firstresult = False

    def _hookexec(
        self,
        hook_name: str,
        methods: Sequence[HookImpl],
        kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        # called from all hookcaller instances.
        # enable_tracing will set its own wrapping function at self._inner_hookexec
>       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)

../lib/python3.11/site-packages/pluggy/_manager.py:115: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

config = <_pytest.config.Config pytest object at [hex]>, op = '=='
left = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1var_...    yyy\n1var_997                        yyy\n1var_998                        yyy\n1var_999                        yyy'
right = 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL\n=============== ============================== =======\n1 var... yyy\n1 var_997                        yyy\n1 var_998                        yyy\n1 var_999                        yyy'

    def pytest_assertrepr_compare(config: Config, op: str, left: object, right: object) -> Optional[List[str]]:
        """Returns explanation for comparisons in failing assert expressions.
    
        If both objects are `str`, uses `difflib.ndiff` to provide explanation.
        """
        if isinstance(left, str) and isinstance(right, str) and op == "==":
            # 16.11.2023, pzotov: we have to put empty string at the beginning of each comparing lists.
            # Otherwise first diff will be at the same line as 'assert' phrase, which causes readability be poor.
            #
            left_lines = ['']
            left_lines.extend(left.splitlines())
            right_lines = ['']
            right_lines.extend(right.splitlines())
    
            # 16.11.2023, pzotov
            # ndiff output must be interpreted as following:
            #     * "E     - <some text>" ==> MISSED line (it was in EXPECTED text but absent in actual one).
            #     * "E     + <some_text>" ==> EXCESSIVE line (it is not in EXPECTED text but did appear in actual).
            # But for QA-purposes, this output must answer the question:
            #     "what must be changed in ACTUAL output so that it became equal to EXPECTED"
            #     (i.e. how to "REVERT" actual back to expected).
            # In order to see such result, we have to specify 'right_lines' to the 1st argument that is passed to ndiff().
            # ::: NB :::
            # We assume that all tests are written so that ACTUAL output is left side in 'assert' statement and EXPECTED
            # is right side, e.g: assert act.clean_stdout == act.clean_expected_stdout
            # This requirement is CRUCIAL if we use ndiff() instead of default pytest comparison method!
            #
>           return list(ndiff(right_lines, left_lines))

src/firebird/qa/plugin.py:608: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]

    def compare(self, a, b):
        r"""
        Compare two sequences of lines; generate the resulting delta.
    
        Each sequence must contain individual single-line strings ending with
        newlines. Such sequences can be obtained from the `readlines()` method
        of file-like objects.  The delta generated also consists of newline-
        terminated strings, ready to be printed as-is via the writelines()
        method of a file-like object.
    
        Example:
    
        >>> print(''.join(Differ().compare('one\ntwo\nthree\n'.splitlines(True),
        ...                                'ore\ntree\nemu\n'.splitlines(True))),
        ...       end="")
        - one
        + ore
        - two
        - three
        + tree
        + emu
        """
    
        cruncher = SequenceMatcher(self.linejunk, a, b)
        for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
            if tag == 'replace':
                g = self._fancy_replace(a, alo, ahi, b, blo, bhi)
            elif tag == 'delete':
                g = self._dump('-', a, alo, ahi)
            elif tag == 'insert':
                g = self._dump('+', b, blo, bhi)
            elif tag == 'equal':
                g = self._dump(' ', a, alo, ahi)
            else:
                raise ValueError('unknown tag %r' % (tag,))
    
>           yield from g

/usr/lib/python3.11/difflib.py:872: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 3, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 3, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 4, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 4, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 4, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 4, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 5, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 5, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 5, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 5, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 6, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 6, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 6, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 6, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 7, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 7, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 7, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 7, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 8, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 8, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 8, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 8, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 9, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 9, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 9, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 9, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 10, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 10, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 10, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 10, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 11, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 11, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 11, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 11, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 12, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 12, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 12, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 12, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 13, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 13, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 13, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 13, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 14, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 14, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 14, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 14, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 15, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 15, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 15, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 15, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 16, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 16, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 16, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 16, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 17, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 17, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 17, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 17, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 18, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 18, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 18, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 18, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 19, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 19, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 19, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 19, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 20, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 20, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 20, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 20, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 21, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 21, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 21, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 21, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 22, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 22, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 22, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 22, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 23, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 23, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 23, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 23, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 26, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 26, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 26, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 26, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 27, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 27, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 27, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 27, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 28, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 28, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 28, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 28, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 29, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 29, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 29, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 29, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 30, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 30, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 30, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 30, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 31, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 31, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 31, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 31, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 32, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 32, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 32, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 32, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 33, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 33, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 33, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 33, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 34, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 34, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 34, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 34, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 35, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 35, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 35, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 35, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 36, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 36, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 36, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 36, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 37, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 37, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 37, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 37, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 38, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 38, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 38, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 38, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 39, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 39, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 39, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 39, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 40, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 40, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 40, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 40, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 41, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 41, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 41, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 41, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 42, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 42, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 42, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 42, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 43, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 43, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 43, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 43, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 44, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 44, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 44, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 44, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 45, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 45, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 45, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 45, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 48, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 48, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 48, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 48, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 49, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 49, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 49, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 49, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 50, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 50, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 50, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 50, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 51, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 51, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 51, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 51, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 52, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 52, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 52, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 52, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 53, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 53, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 53, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 53, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 54, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 54, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 54, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 54, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 55, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 55, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 55, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 55, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 56, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 56, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 56, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 56, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 57, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 57, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 57, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 57, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 58, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 58, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 58, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 58, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 59, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 59, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 59, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 59, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 60, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 60, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 60, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 60, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 61, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 61, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 61, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 61, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 62, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 62, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 62, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 62, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 63, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 63, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 63, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 63, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 64, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 64, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 64, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 64, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 65, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 65, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 65, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 65, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 66, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 66, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 66, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 66, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 67, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 67, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 67, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 67, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 70, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 70, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 70, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 70, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 71, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 71, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 71, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 71, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 72, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 72, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 72, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 72, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 73, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 73, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 73, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 73, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 74, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 74, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 74, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 74, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 75, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 75, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 75, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 75, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 76, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 76, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 76, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 76, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 77, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 77, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 77, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 77, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 78, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 78, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 78, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 78, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 79, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 79, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 79, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 79, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 80, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 80, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 80, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 80, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 81, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 81, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 81, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 81, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 82, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 82, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 82, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 82, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 83, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 83, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 83, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 83, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 84, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 84, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 84, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 84, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 85, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 85, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 85, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 85, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 86, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 86, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 86, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 86, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 87, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 87, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 87, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 87, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 88, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 88, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 88, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 88, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 89, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 89, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 89, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 89, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 92, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 92, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 92, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 92, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 93, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 93, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 93, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 93, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 94, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 94, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 94, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 94, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 95, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 95, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 95, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 95, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 96, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 96, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 96, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 96, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 97, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 97, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 97, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 97, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 98, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 98, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 98, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 98, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 99, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 99, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 99, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 99, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 100, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 100, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 100, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 100, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 101, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 101, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 101, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 101, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 102, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 102, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 102, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 102, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 103, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 103, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 103, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 103, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 104, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 104, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 104, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 104, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 105, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 105, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 105, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 105, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 106, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 106, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 106, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 106, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 107, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 107, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 107, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 107, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 108, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 108, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 108, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 108, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 109, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 109, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 109, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 109, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 110, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 110, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 110, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 110, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 111, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 111, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 111, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 111, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 114, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 114, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 114, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 114, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 115, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 115, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 115, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 115, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 116, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 116, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 116, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 116, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 117, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 117, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 117, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 117, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 118, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 118, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 118, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 118, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 119, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 119, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 119, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 119, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 120, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 120, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 120, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 120, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 121, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 121, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 121, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 121, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 122, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 122, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 122, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 122, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 123, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 123, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 123, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 123, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 124, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 124, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 124, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 124, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 125, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 125, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 125, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 125, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 126, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 126, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 126, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 126, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 127, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 127, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 127, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 127, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 128, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 128, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 128, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 128, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 129, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 129, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 129, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 129, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 130, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 130, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 130, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 130, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 131, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 131, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 131, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 131, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 132, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 132, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 132, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 132, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 133, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 133, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 133, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 133, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 136, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 136, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 136, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 136, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 137, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 137, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 137, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 137, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 138, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 138, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 138, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 138, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 139, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 139, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 139, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 139, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 140, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 140, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 140, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 140, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 141, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 141, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 141, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 141, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 142, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 142, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 142, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 142, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 143, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 143, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 143, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 143, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 144, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 144, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 144, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 144, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 145, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 145, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 145, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 145, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 146, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 146, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 146, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 146, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 147, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 147, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 147, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 147, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 148, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 148, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 148, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 148, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 149, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 149, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 149, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 149, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 150, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 150, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 150, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 150, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 151, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 151, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 151, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 151, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 152, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 152, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 152, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 152, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 153, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 153, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 153, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 153, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 154, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 154, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 154, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 154, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 155, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 155, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 155, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 155, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 158, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 158, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 158, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 158, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 159, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 159, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 159, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 159, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 160, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 160, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 160, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 160, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 161, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 161, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 161, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 161, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 162, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 162, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 162, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 162, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 163, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 163, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 163, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 163, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 164, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 164, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 164, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 164, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 165, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 165, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 165, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 165, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 166, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 166, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 166, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 166, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 167, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 167, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 167, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 167, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 168, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 168, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 168, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 168, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 169, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 169, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 169, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 169, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 170, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 170, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 170, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 170, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 171, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 171, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 171, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 171, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 172, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 172, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 172, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 172, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 173, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 173, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 173, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 173, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 174, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 174, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 174, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 174, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 175, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 175, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 175, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 175, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 176, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 176, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 176, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 176, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 177, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 177, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 177, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 177, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 180, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 180, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 180, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 180, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 181, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 181, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 181, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 181, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 182, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 182, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 182, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 182, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 183, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 183, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 183, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 183, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 184, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 184, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 184, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 184, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 185, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 185, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 185, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 185, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 186, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 186, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 186, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 186, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 187, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 187, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 187, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 187, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 188, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 188, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 188, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 188, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 189, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 189, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 189, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 189, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 190, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 190, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 190, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 190, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 191, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 191, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 191, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 191, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 192, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 192, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 192, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 192, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 193, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 193, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 193, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 193, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 194, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 194, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 194, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 194, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 195, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 195, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 195, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 195, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 196, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 196, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 196, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 196, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 197, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 197, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 197, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 197, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 198, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 198, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 198, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 198, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 199, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 199, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 199, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 199, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 202, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 202, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 202, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 202, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 203, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 203, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 203, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 203, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 204, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 204, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 204, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 204, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 205, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 205, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 205, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 205, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 206, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 206, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 206, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 206, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 207, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 207, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 207, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 207, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 208, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 208, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 208, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 208, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 209, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 209, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 209, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 209, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 210, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 210, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 210, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 210, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 211, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 211, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 211, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 211, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 212, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 212, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 212, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 212, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 213, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 213, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 213, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 213, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 214, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 214, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 214, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 214, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 215, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 215, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 215, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 215, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 216, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 216, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 216, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 216, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 217, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 217, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 217, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 217, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 218, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 218, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 218, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 218, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 219, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 219, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 219, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 219, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 220, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 220, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 220, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 220, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 221, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 221, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 221, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 221, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 224, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 224, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 224, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 224, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 225, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 225, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 225, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 225, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 226, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 226, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 226, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 226, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 227, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 227, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 227, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 227, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 228, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 228, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 228, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 228, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 229, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 229, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 229, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 229, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 230, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 230, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 230, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 230, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 231, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 231, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 231, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 231, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 232, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 232, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 232, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 232, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 233, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 233, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 233, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 233, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 234, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 234, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 234, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 234, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 235, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 235, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 235, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 235, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 236, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 236, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 236, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 236, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 237, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 237, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 237, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 237, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 238, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 238, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 238, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 238, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 239, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 239, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 239, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 239, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 240, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 240, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 240, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 240, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 241, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 241, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 241, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 241, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 242, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 242, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 242, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 242, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 243, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 243, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 243, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 243, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 246, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 246, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 246, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 246, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 247, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 247, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 247, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 247, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 248, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 248, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 248, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 248, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 249, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 249, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 249, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 249, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 250, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 250, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 250, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 250, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 251, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 251, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 251, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 251, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 252, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 252, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 252, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 252, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 253, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 253, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 253, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 253, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 254, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 254, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 254, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 254, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 255, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 255, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 255, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 255, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 256, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 256, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 256, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 256, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 257, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 257, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 257, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 257, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 258, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 258, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 258, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 258, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 259, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 259, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 259, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 259, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 260, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 260, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 260, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 260, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 261, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 261, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 261, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 261, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 262, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 262, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 262, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 262, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 263, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 263, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 263, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 263, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 264, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 264, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 264, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 264, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 265, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 265, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 265, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 265, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 268, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 268, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 268, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 268, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 269, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 269, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 269, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 269, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 270, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 270, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 270, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 270, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 271, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 271, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 271, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 271, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 272, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 272, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 272, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 272, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 273, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 273, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 273, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 273, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 274, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 274, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 274, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 274, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 275, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 275, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 275, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 275, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 276, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 276, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 276, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 276, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 277, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 277, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 277, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 277, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 278, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 278, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 278, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 278, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 279, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 279, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 279, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 279, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 280, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 280, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 280, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 280, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 281, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 281, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 281, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 281, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 282, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 282, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 282, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 282, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 283, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 283, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 283, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 283, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 284, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 284, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 284, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 284, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 285, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 285, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 285, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 285, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 286, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 286, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 286, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 286, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 287, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 287, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 287, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 287, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 290, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 290, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 290, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 290, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 291, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 291, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 291, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 291, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 292, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 292, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 292, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 292, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 293, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 293, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 293, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 293, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 294, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 294, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 294, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 294, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 295, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 295, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 295, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 295, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 296, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 296, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 296, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 296, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 297, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 297, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 297, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 297, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 298, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 298, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 298, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 298, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 299, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 299, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 299, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 299, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 300, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 300, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 300, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 300, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 301, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 301, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 301, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 301, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 302, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 302, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 302, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 302, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 303, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 303, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 303, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 303, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 304, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 304, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 304, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 304, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 305, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 305, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 305, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 305, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 306, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 306, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 306, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 306, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 307, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 307, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 307, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 307, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 308, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 308, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 308, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 308, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 309, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 309, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 309, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 309, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 312, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 312, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 312, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 312, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 313, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 313, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 313, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 313, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 314, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 314, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 314, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 314, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 315, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 315, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 315, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 315, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 316, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 316, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 316, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 316, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 317, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 317, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 317, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 317, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 318, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 318, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 318, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 318, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 319, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 319, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 319, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 319, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 320, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 320, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 320, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 320, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 321, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 321, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 321, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 321, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 322, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 322, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 322, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 322, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 323, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 323, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 323, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 323, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 324, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 324, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 324, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 324, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 325, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 325, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 325, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 325, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 326, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 326, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 326, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 326, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 327, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 327, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 327, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 327, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 328, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 328, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 328, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 328, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 329, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 329, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 329, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 329, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 330, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 330, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 330, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 330, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 331, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 331, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 331, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 331, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 334, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 334, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 334, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 334, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 335, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 335, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 335, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 335, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 336, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 336, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 336, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 336, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 337, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 337, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 337, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 337, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 338, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 338, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 338, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 338, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 339, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 339, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 339, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 339, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 340, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 340, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 340, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 340, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 341, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 341, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 341, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 341, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 342, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 342, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 342, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 342, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 343, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 343, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 343, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 343, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 344, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 344, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 344, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 344, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 345, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 345, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 345, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 345, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 346, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 346, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 346, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 346, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 347, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 347, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 347, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 347, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 348, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 348, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 348, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 348, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 349, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 349, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 349, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 349, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 350, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 350, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 350, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 350, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 351, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 351, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 351, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 351, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 352, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 352, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 352, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 352, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 353, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 353, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 353, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 353, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 356, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 356, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 356, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 356, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 357, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 357, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 357, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 357, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 358, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 358, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 358, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 358, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 359, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 359, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 359, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 359, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 360, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 360, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 360, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 360, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 361, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 361, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 361, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 361, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 362, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 362, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 362, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 362, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 363, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 363, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 363, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 363, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 364, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 364, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 364, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 364, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 365, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 365, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 365, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 365, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 366, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 366, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 366, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 366, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 367, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 367, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 367, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 367, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 368, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 368, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 368, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 368, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 369, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 369, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 369, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 369, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 370, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 370, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 370, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 370, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 371, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 371, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 371, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 371, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 372, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 372, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 372, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 372, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 373, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 373, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 373, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 373, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 374, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 374, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 374, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 374, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 375, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 375, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 375, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 375, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 378, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 378, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 378, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 378, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 379, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 379, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 379, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 379, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 380, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 380, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 380, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 380, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 381, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 381, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 381, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 381, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 382, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 382, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 382, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 382, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 383, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 383, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 383, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 383, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 384, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 384, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 384, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 384, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 385, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 385, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 385, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 385, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 386, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 386, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 386, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 386, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 387, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 387, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 387, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 387, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 388, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 388, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 388, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 388, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 389, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 389, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 389, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 389, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 390, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 390, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 390, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 390, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 391, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 391, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 391, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 391, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 392, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 392, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 392, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 392, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 393, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 393, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 393, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 393, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 394, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 394, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 394, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 394, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 395, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 395, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 395, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 395, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 396, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 396, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 396, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 396, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 397, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 397, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 397, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 397, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 400, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 400, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 400, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 400, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 401, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 401, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 401, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 401, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 402, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 402, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 402, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 402, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 403, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 403, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 403, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 403, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 404, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 404, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 404, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 404, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 405, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 405, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 405, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 405, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 406, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 406, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 406, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 406, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 407, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 407, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 407, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 407, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 408, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 408, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 408, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 408, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 409, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 409, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 409, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 409, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 410, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 410, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 410, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 410, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 411, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 411, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 411, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 411, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 412, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 412, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 412, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 412, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 413, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 413, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 413, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 413, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 414, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 414, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 414, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 414, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 415, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 415, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 415, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 415, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 416, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 416, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 416, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 416, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 417, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 417, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 417, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 417, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 418, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 418, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 418, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 418, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 419, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 419, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 419, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 419, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 422, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 422, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 422, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 422, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 423, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 423, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 423, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 423, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 424, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 424, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 424, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 424, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 425, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 425, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 425, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 425, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 426, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 426, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 426, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 426, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 427, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 427, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 427, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 427, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 428, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 428, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 428, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 428, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 429, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 429, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 429, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 429, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 430, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 430, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 430, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 430, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 431, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 431, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 431, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 431, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 432, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 432, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 432, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 432, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 433, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 433, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 433, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 433, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 434, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 434, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 434, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 434, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 435, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 435, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 435, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 435, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 436, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 436, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 436, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 436, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 437, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 437, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 437, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 437, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 438, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 438, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 438, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 438, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 439, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 439, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 439, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 439, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 440, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 440, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 440, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 440, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 441, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 441, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 441, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 441, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 444, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 444, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 444, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 444, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 445, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 445, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 445, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 445, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 446, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 446, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 446, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 446, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 447, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 447, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 447, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 447, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 448, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 448, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 448, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 448, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 449, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 449, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 449, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 449, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 450, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 450, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 450, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 450, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 451, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 451, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 451, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 451, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 452, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 452, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 452, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 452, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 453, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 453, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 453, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 453, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 454, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 454, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 454, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 454, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 455, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 455, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 455, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 455, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 456, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 456, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 456, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 456, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 457, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 457, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 457, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 457, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 458, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 458, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 458, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 458, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 459, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 459, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 459, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 459, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 460, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 460, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 460, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 460, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 461, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 461, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 461, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 461, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 462, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 462, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 462, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 462, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 463, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 463, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 463, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 463, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 466, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 466, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 466, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 466, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 467, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 467, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 467, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 467, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 468, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 468, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 468, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 468, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 469, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 469, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 469, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 469, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 470, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 470, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 470, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 470, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 471, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 471, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 471, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 471, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 472, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 472, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 472, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 472, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 473, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 473, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 473, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 473, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 474, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 474, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 474, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 474, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 475, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 475, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 475, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 475, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 476, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 476, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 476, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 476, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 477, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 477, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 477, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 477, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 478, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 478, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 478, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 478, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 479, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 479, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 479, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 479, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 480, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 480, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 480, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 480, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 481, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 481, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 481, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 481, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 482, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 482, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 482, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 482, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 483, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 483, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 483, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 483, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 484, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 484, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 484, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 484, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 485, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 485, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 485, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 485, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 488, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 488, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 488, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 488, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 489, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 489, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 489, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 489, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 490, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 490, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 490, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 490, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 491, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 491, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 491, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 491, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 492, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 492, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 492, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 492, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 493, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 493, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 493, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 493, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 494, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 494, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 494, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 494, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 495, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 495, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 495, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 495, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 496, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 496, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 496, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 496, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 497, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 497, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 497, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 497, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 498, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 498, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 498, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 498, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 499, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 499, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 499, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 499, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 500, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 500, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 500, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 500, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 501, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 501, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 501, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 501, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 502, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 502, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 502, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 502, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 503, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 503, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 503, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 503, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 504, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 504, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 504, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 504, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 505, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 505, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 505, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 505, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 506, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 506, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 506, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 506, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 507, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 507, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 507, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 507, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 510, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 510, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 510, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 510, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 511, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 511, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 511, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 511, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 512, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 512, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 512, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 512, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 513, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 513, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 513, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 513, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 514, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 514, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 514, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 514, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 515, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 515, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 515, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 515, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 516, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 516, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 516, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 516, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 517, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 517, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 517, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 517, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 518, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 518, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 518, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 518, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 519, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 519, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 519, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 519, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 520, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 520, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 520, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 520, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 521, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 521, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 521, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 521, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 522, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 522, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 522, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 522, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 523, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 523, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 523, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 523, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:985: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 524, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 524, bhi = 1101

    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)
    
>       yield from g

/usr/lib/python3.11/difflib.py:997: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.Differ pytest object at [hex]>
a = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...r_0                          yyy', '1 var_1                          yyy', '1 var_2                          yyy', ...]
alo = 524, ahi = 1101
b = ['', 'WAS_OVERWRITTEN CTX_KEY                        CTX_VAL', '=============== ============================== =======...var_0                          yyy', '1var_1                          yyy', '1var_2                          yyy', ...]
blo = 524, bhi = 1101

    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)

/usr/lib/python3.11/difflib.py:915: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.SequenceMatcher pytest object at [hex]>
isjunk = <function IS_CHARACTER_JUNK at 0x7fee4cf1b4c0>, a = '', b = ''
autojunk = True

    def __init__(self, isjunk=None, a='', b='', autojunk=True):
        """Construct a SequenceMatcher.
    
        Optional arg isjunk is None (the default), or a one-argument
        function that takes a sequence element and returns true iff the
        element is junk.  None is equivalent to passing "lambda x: 0", i.e.
        no elements are considered to be junk.  For example, pass
            lambda x: x in " \\t"
        if you're comparing lines as sequences of characters, and don't
        want to synch up on blanks or hard tabs.
    
        Optional arg a is the first of two sequences to be compared.  By
        default, an empty string.  The elements of a must be hashable.  See
        also .set_seqs() and .set_seq1().
    
        Optional arg b is the second of two sequences to be compared.  By
        default, an empty string.  The elements of b must be hashable. See
        also .set_seqs() and .set_seq2().
    
        Optional arg autojunk should be set to False to disable the
        "automatic junk heuristic" that treats popular elements as junk
        (see module documentation for more information).
        """
    
        # Members:
        # a
        #      first sequence
        # b
        #      second sequence; differences are computed as "what do
        #      we need to do to 'a' to change it into 'b'?"
        # b2j
        #      for x in b, b2j[x] is a list of the indices (into b)
        #      at which x appears; junk and popular elements do not appear
        # fullbcount
        #      for x in b, fullbcount[x] == the number of times x
        #      appears in b; only materialized if really needed (used
        #      only for computing quick_ratio())
        # matching_blocks
        #      a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k];
        #      ascending & non-overlapping in i and in j; terminated by
        #      a dummy (len(a), len(b), 0) sentinel
        # opcodes
        #      a list of (tag, i1, i2, j1, j2) tuples, where tag is
        #      one of
        #          'replace'   a[i1:i2] should be replaced by b[j1:j2]
        #          'delete'    a[i1:i2] should be deleted
        #          'insert'    b[j1:j2] should be inserted
        #          'equal'     a[i1:i2] == b[j1:j2]
        # isjunk
        #      a user-supplied function taking a sequence element and
        #      returning true iff the element is "junk" -- this has
        #      subtle but helpful effects on the algorithm, which I'll
        #      get around to writing up someday <0.9 wink>.
        #      DON'T USE!  Only __chain_b uses this.  Use "in self.bjunk".
        # bjunk
        #      the items in b for which isjunk is True.
        # bpopular
        #      nonjunk items in b treated as junk by the heuristic (if used).
    
        self.isjunk = isjunk
        self.a = self.b = None
        self.autojunk = autojunk
>       self.set_seqs(a, b)

/usr/lib/python3.11/difflib.py:182: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.SequenceMatcher pytest object at [hex]>, a = '', b = ''

    def set_seqs(self, a, b):
        """Set the two sequences to be compared.
    
        >>> s = SequenceMatcher()
        >>> s.set_seqs("abcd", "bcde")
        >>> s.ratio()
        0.75
        """
    
        self.set_seq1(a)
>       self.set_seq2(b)

/usr/lib/python3.11/difflib.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <difflib.SequenceMatcher pytest object at [hex]>, b = ''

    def set_seq2(self, b):
        """Set the second sequence to be compared.
    
        The first sequence to be compared is not changed.
    
        >>> s = SequenceMatcher(None, "abcd", "bcde")
        >>> s.ratio()
        0.75
        >>> s.set_seq2("abcd")
        >>> s.ratio()
        1.0
        >>>
    
        SequenceMatcher computes and caches detailed information about the
        second sequence, so if you want to compare one sequence S against
        many sequences, use .set_seq2(S) once and call .set_seq1(x)
        repeatedly for each of the other sequences.
    
        See also set_seqs() and set_seq1().
        """
    
        if b is self.b:
            return
        self.b = b
        self.matching_blocks = self.opcodes = None
        self.fullbcount = None
>       self.__chain_b()
E       RecursionError: maximum recursion depth exceeded

/usr/lib/python3.11/difflib.py:248: RecursionError
Full history of outcomes and elapsed time, ms:
NN SNAP_INFO CS_outcome SS_outcome CS_run_time SS_run_time CS_run_beg CS_run_end SS_run_beg SS_run_end
1 6.0.0.892 2025.07.02 10f7d P P 1097 421 2025.07.03 00:07:24.404 2025.07.03 00:07:25.501 2025.07.02 21:27:49.903 2025.07.02 21:27:50.324
2 6.0.0.889 2025.07.01 969ac F F 229309 238768 2025.07.02 00:30:19.721 2025.07.02 00:34:09.030 2025.07.01 21:28:41.456 2025.07.01 21:32:40.224
3 6.0.0.884 2025.06.30 f7e5f F F 229984 232930 2025.07.01 00:33:40.319 2025.07.01 00:37:30.303 2025.06.30 21:37:43.655 2025.06.30 21:41:36.585
4 6.0.0.881 2025.06.27 7035d P P 624 427 2025.06.30 00:22:17.294 2025.06.30 00:22:17.918 2025.06.29 21:28:59.523 2025.06.29 21:28:59.950
5 6.0.0.877 2025.06.26 8e38f P P 982 413 2025.06.27 00:06:46.521 2025.06.27 00:06:47.503 2025.06.26 21:27:39.531 2025.06.26 21:27:39.944
6 6.0.0.876 2025.06.25 b1bec P P 639 397 2025.06.26 00:10:12.625 2025.06.26 00:10:13.264 2025.06.25 21:28:21.836 2025.06.25 21:28:22.233
7 6.0.0.863 2025.06.24 c3c20 P P 672 347 2025.06.25 00:11:08.111 2025.06.25 00:11:08.783 2025.06.24 21:27:43.261 2025.06.24 21:27:43.608
8 6.0.0.858 2025.06.23 8d6f7 P P 580 372 2025.06.24 00:10:14.990 2025.06.24 00:10:15.570 2025.06.23 21:28:17.345 2025.06.23 21:28:17.717
9 6.0.0.849 2025.06.20 7b79c P P 1095 459 2025.06.21 00:20:16.542 2025.06.21 00:20:17.637 2025.06.20 21:30:51.709 2025.06.20 21:30:52.168
10 6.0.0.848 2025.06.19 c483c P P 633 495 2025.06.20 00:16:30.961 2025.06.20 00:16:31.594 2025.06.19 21:29:56.751 2025.06.19 21:29:57.246
11 6.0.0.845 2025.06.18 22b12 P P 646 470 2025.06.19 00:22:37.922 2025.06.19 00:22:38.568 2025.06.18 21:29:04.072 2025.06.18 21:29:04.542
12 6.0.0.843 2025.06.16 995f4 P P 1205 463 2025.06.18 00:25:25.290 2025.06.18 00:25:26.495 2025.06.17 21:31:12.726 2025.06.17 21:31:13.189
13 6.0.0.840 2025.06.14 29bca P P 667 463 2025.06.16 00:15:06.255 2025.06.16 00:15:06.922 2025.06.15 21:27:53.693 2025.06.15 21:27:54.156
14 6.0.0.838 2025.06.13 0e28a P P 1142 526 2025.06.14 00:25:27.768 2025.06.14 00:25:28.910 2025.06.13 21:29:15.265 2025.06.13 21:29:15.791
15 6.0.0.835 2025.06.12 2cf29 P P 653 528 2025.06.13 00:27:18.341 2025.06.13 00:27:18.994 2025.06.12 21:34:54.952 2025.06.12 21:34:55.480
16 6.0.0.834 2025.06.11 e889f P P 880 448 2025.06.12 00:23:45.114 2025.06.12 00:23:45.994 2025.06.11 21:30:52.850 2025.06.11 21:30:53.298
17 6.0.0.800 2025.06.10 1f226 P P 1136 417 2025.06.10 23:22:08.698 2025.06.10 23:22:09.834 2025.06.10 21:29:47.621 2025.06.10 21:29:48.038
18 6.0.0.799 2025.06.07 be644 P P 1102 426 2025.06.09 23:21:27.531 2025.06.09 23:21:28.633 2025.06.09 21:29:37.264 2025.06.09 21:29:37.690
19 6.0.0.797 2025.06.06 303e8 P P 1074 505 2025.06.06 23:22:20.634 2025.06.06 23:22:21.708 2025.06.06 21:26:28.625 2025.06.06 21:26:29.130
20 6.0.0.795 2025.05.29 7a71a P P 1205 432 2025.06.05 23:19:28.311 2025.06.05 23:19:29.516 2025.06.05 21:26:22.877 2025.06.05 21:26:23.309
21 6.0.0.792 2025.05.28 b4327 P P 1212 497 2025.05.28 23:27:01.623 2025.05.28 23:27:02.835 2025.05.28 21:27:10.131 2025.05.28 21:27:10.628
22 6.0.0.791 2025.05.27 02db8 P P 801 492 2025.05.27 23:25:21.118 2025.05.27 23:25:21.919 2025.05.27 21:26:52.154 2025.05.27 21:26:52.646
23 6.0.0.789 2025.05.21 64051 P P 1170 508 2025.05.24 23:22:49.655 2025.05.24 23:22:50.825 2025.05.24 21:26:41.990 2025.05.24 21:26:42.498
24 6.0.0.787 2025.05.20 230ad P P 1206 472 2025.05.20 23:20:16.569 2025.05.20 23:20:17.775 2025.05.20 21:26:29.036 2025.05.20 21:26:29.508
25 6.0.0.783 2025.05.12 37320 P P 705 448 2025.05.19 11:03:15.733 2025.05.19 11:03:16.438 2025.05.19 09:18:13.947 2025.05.19 09:18:14.395
26 6.0.0.779 2025.05.11 136fa P P 948 498 2025.05.11 23:17:41.647 2025.05.11 23:17:42.595 2025.05.11 21:26:10.649 2025.05.11 21:26:11.147
27 6.0.0.778 2025.05.07 d735e P P 1088 428 2025.05.07 23:16:44.810 2025.05.07 23:16:45.898 2025.05.07 21:26:17.582 2025.05.07 21:26:18.010
28 6.0.0.776 2025.05.06 007cd P P 1204 458 2025.05.06 23:10:16.506 2025.05.06 23:10:17.710 2025.05.06 21:26:13.438 2025.05.06 21:26:13.896
29 6.0.0.770 2025.05.05 82c4a P P 1153 394 2025.05.05 23:09:16.401 2025.05.05 23:09:17.554 2025.05.05 21:26:01.682 2025.05.05 21:26:02.076
30 6.0.0.767 2025.05.01 cdd29 P P 1204 501 2025.05.01 23:07:54.014 2025.05.01 23:07:55.218 2025.05.01 21:25:54.198 2025.05.01 21:25:54.699
31 6.0.0.762 2025.04.30 5cb15 P P 1059 463 2025.04.30 23:05:27.140 2025.04.30 23:05:28.199 2025.04.30 21:25:49.951 2025.04.30 21:25:50.414
32 6.0.0.755 2025.04.29 739c6 P P 1101 473 2025.04.29 23:07:32.805 2025.04.29 23:07:33.906 2025.04.29 21:26:00.214 2025.04.29 21:26:00.687
33 6.0.0.753 2025.04.27 29ab3 P P 1077 469 2025.04.27 23:07:04.578 2025.04.27 23:07:05.655 2025.04.27 21:25:58.599 2025.04.27 21:25:59.068
34 6.0.0.745 2025.04.21 78ad8 P P 667 475 2025.04.25 23:07:28.124 2025.04.25 23:07:28.791 2025.04.25 21:27:07.112 2025.04.25 21:27:07.587
35 6.0.0.744 2025.04.19 e883a P P 1199 484 2025.04.19 23:08:46.216 2025.04.19 23:08:47.415 2025.04.19 21:26:08.692 2025.04.19 21:26:09.176
36 6.0.0.742 2025.04.17 abc3b P P 1072 501 2025.04.18 23:09:15.614 2025.04.18 23:09:16.686 2025.04.18 21:26:06.453 2025.04.18 21:26:06.954
37 6.0.0.737 2025.04.16 fe52b P P 1093 472 2025.04.16 23:08:57.059 2025.04.16 23:08:58.152 2025.04.16 21:26:06.058 2025.04.16 21:26:06.530
38 6.0.0.736 2025.04.14 3e6be P P 1131 438 2025.04.14 22:55:49.110 2025.04.14 22:55:50.241 2025.04.14 21:24:57.492 2025.04.14 21:24:57.930
39 6.0.0.735 2025.04.13 6635c P P 1171 446 2025.04.13 22:57:05.795 2025.04.13 22:57:06.966 2025.04.13 21:25:26.334 2025.04.13 21:25:26.780
40 6.0.0.734 2025.04.12 12f3f P P 1144 407 2025.04.12 22:56:00.531 2025.04.12 22:56:01.675 2025.04.12 21:25:05.878 2025.04.12 21:25:06.285
41 6.0.0.730 2025.04.11 240b8 P P 1040 380 2025.04.11 22:56:56.210 2025.04.11 22:56:57.250 2025.04.11 21:25:01.572 2025.04.11 21:25:01.952
42 6.0.0.726 2025.04.10 d79c6 P P 1100 390 2025.04.10 22:56:38.610 2025.04.10 22:56:39.710 2025.04.10 21:25:09.077 2025.04.10 21:25:09.467
43 6.0.0.725 2025.04.09 a2b05 P P 997 406 2025.04.09 22:56:45.104 2025.04.09 22:56:46.101 2025.04.09 21:25:22.539 2025.04.09 21:25:22.945
44 6.0.0.722 2025.04.08 a8b86 P P 1143 490 2025.04.08 23:06:41.405 2025.04.08 23:06:42.548 2025.04.08 21:26:11.439 2025.04.08 21:26:11.929
45 6.0.0.719 2025.04.06 90fd9 P P 1051 442 2025.04.06 23:04:38.306 2025.04.06 23:04:39.357 2025.04.06 21:26:12.140 2025.04.06 21:26:12.582
46 6.0.0.717 2025.04.04 53d70 P P 1131 441 2025.04.04 23:02:24.471 2025.04.04 23:02:25.602 2025.04.04 21:25:46.014 2025.04.04 21:25:46.455
47 6.0.0.716 2025.04.03 fc636 P P 1086 468 2025.04.03 23:05:42.527 2025.04.03 23:05:43.613 2025.04.03 21:26:04.761 2025.04.03 21:26:05.229
48 6.0.0.715 2025.04.02 907ed P P 1044 408 2025.04.02 23:05:30.886 2025.04.02 23:05:31.930 2025.04.02 21:26:16.986 2025.04.02 21:26:17.394
49 6.0.0.710 2025.04.01 40651 P P 1131 405 2025.04.01 23:04:28.186 2025.04.01 23:04:29.317 2025.04.01 21:25:58.796 2025.04.01 21:25:59.201
50 6.0.0.708 2025.03.31 cb069 P P 1138 451 2025.03.31 22:57:45.221 2025.03.31 22:57:46.359 2025.03.31 21:25:29.584 2025.03.31 21:25:30.035
51 6.0.0.707 2025.03.28 4bd4f P P 1037 386 2025.03.30 22:57:37.755 2025.03.30 22:57:38.792 2025.03.30 21:25:21.721 2025.03.30 21:25:22.107
52 6.0.0.698 2025.03.26 d72a7 P P 1183 439 2025.03.27 23:08:36.859 2025.03.27 23:08:38.042 2025.03.27 21:26:24.927 2025.03.27 21:26:25.366
53 6.0.0.693 2025.03.24 0b559 P P 1169 385 2025.03.24 23:02:02.566 2025.03.24 23:02:03.735 2025.03.24 21:26:30.968 2025.03.24 21:26:31.353
54 6.0.0.687 2025.03.22 730aa P P 1170 487 2025.03.23 23:11:10.427 2025.03.23 23:11:11.597 2025.03.23 21:26:06.202 2025.03.23 21:26:06.689
55 6.0.0.686 2025.03.20 71bf6 P P 1130 494 2025.03.20 23:14:48.822 2025.03.20 23:14:49.952 2025.03.20 21:26:35.600 2025.03.20 21:26:36.094
56 6.0.0.685 2025.03.19 a8577 P P 1133 444 2025.03.19 23:12:53.639 2025.03.19 23:12:54.772 2025.03.19 21:26:21.310 2025.03.19 21:26:21.754
57 6.0.0.680 2025.03.18 90d29 P P 877 458 2025.03.19 09:50:14.108 2025.03.19 09:50:14.985 2025.03.19 08:16:03.178 2025.03.19 08:16:03.636
58 6.0.0.677 2025.03.16 c0a60 P P 1409 567 2025.03.16 23:06:14.694 2025.03.16 23:06:16.103 2025.03.16 21:26:19.575 2025.03.16 21:26:20.142
59 6.0.0.676 2025.03.15 3034f P P 895 530 2025.03.16 14:52:03.764 2025.03.16 14:52:04.659 2025.03.16 13:21:50.036 2025.03.16 13:21:50.566
60 6.0.0.673 2025.03.13 40f5b P P 1356 630 2025.03.13 23:05:20.871 2025.03.13 23:05:22.227 2025.03.13 21:26:01.476 2025.03.13 21:26:02.106
61 6.0.0.671 2025.03.12 a4fff P P 1337 502 2025.03.12 23:09:06.989 2025.03.12 23:09:08.326 2025.03.12 21:26:33.036 2025.03.12 21:26:33.538
62 6.0.0.663 2025.03.11 daad2 P P 1318 550 2025.03.11 23:07:22.884 2025.03.11 23:07:24.202 2025.03.11 21:26:47.615 2025.03.11 21:26:48.165
63 6.0.0.661 2025.03.07 b9869 P P 1232 564 2025.03.10 22:54:44.558 2025.03.10 22:54:45.790 2025.03.10 21:25:13.325 2025.03.10 21:25:13.889
64 6.0.0.660 2025.03.04 a6700 P P 1271 572 2025.03.06 23:00:46.046 2025.03.06 23:00:47.317 2025.03.06 21:25:46.672 2025.03.06 21:25:47.244
65 6.0.0.658 2025.03.03 f15f8 P P 1368 508 2025.03.03 22:56:30.056 2025.03.03 22:56:31.424 2025.03.03 21:25:35.810 2025.03.03 21:25:36.318
66 6.0.0.656 2025.02.27 25fb4 P P 1463 577 2025.03.02 23:11:43.140 2025.03.02 23:11:44.603 2025.03.02 21:26:57.611 2025.03.02 21:26:58.188
67 6.0.0.655 2025.02.25 6e3e0 P P 1315 511 2025.02.26 22:58:04.594 2025.02.26 22:58:05.909 2025.02.26 21:25:36.254 2025.02.26 21:25:36.765
68 6.0.0.654 2025.02.24 b7141 P P 1378 483 2025.02.24 23:00:07.974 2025.02.24 23:00:09.352 2025.02.24 21:25:51.378 2025.02.24 21:25:51.861
69 6.0.0.652 2025.02.22 22662 P P 782 447 2025.02.24 06:25:43.089 2025.02.24 06:25:43.871 2025.02.23 23:42:22.724 2025.02.23 23:42:23.171
70 6.0.0.647 2025.02.21 9fccb P P 1263 628 2025.02.21 23:03:09.531 2025.02.21 23:03:10.794 2025.02.21 21:29:19.701 2025.02.21 21:29:20.329
71 6.0.0.640 2025.02.19 9b8ac P P 1345 530 2025.02.19 22:58:29.933 2025.02.19 22:58:31.278 2025.02.19 21:25:57.853 2025.02.19 21:25:58.383
72 6.0.0.639 2025.02.18 201a4 P P 1350 529 2025.02.18 22:55:21.671 2025.02.18 22:55:23.021 2025.02.18 21:25:38.708 2025.02.18 21:25:39.237
73 6.0.0.637 2025.02.12 6d0f5 P P 1312 552 2025.02.13 23:05:40.175 2025.02.13 23:05:41.487 2025.02.13 21:26:18.054 2025.02.13 21:26:18.606
74 6.0.0.636 2025.02.11 0424f P P 1817 518 2025.02.11 23:04:30.034 2025.02.11 23:04:31.851 2025.02.11 21:25:48.009 2025.02.11 21:25:48.527
75 6.0.0.635 2025.02.10 f640f P P 1539 485 2025.02.10 23:04:14.481 2025.02.10 23:04:16.020 2025.02.10 21:26:25.285 2025.02.10 21:26:25.770
76 6.0.0.629 2025.02.07 194f9 P P 1313 513 2025.02.07 23:02:54.141 2025.02.07 23:02:55.454 2025.02.07 21:26:04.359 2025.02.07 21:26:04.872
77 6.0.0.628 2025.02.06 859d5 P P 850 636 2025.02.06 23:09:25.816 2025.02.06 23:09:26.666 2025.02.06 21:25:54.168 2025.02.06 21:25:54.804
78 6.0.0.621 2025.02.05 34fe7 P P 1465 595 2025.02.05 23:10:19.279 2025.02.05 23:10:20.744 2025.02.05 21:26:00.539 2025.02.05 21:26:01.134
79 6.0.0.609 2025.02.04 76d57 P P 830 599 2025.02.04 23:05:19.070 2025.02.04 23:05:19.900 2025.02.04 21:25:57.654 2025.02.04 21:25:58.253
80 6.0.0.607 2025.02.03 1985b P P 1080 696 2025.02.03 23:06:01.230 2025.02.03 23:06:02.310 2025.02.03 21:26:20.475 2025.02.03 21:26:21.171
81 6.0.0.601 2025.02.01 6af07 P P 1249 488 2025.02.01 23:01:55.225 2025.02.01 23:01:56.474 2025.02.01 21:25:44.876 2025.02.01 21:25:45.364
82 6.0.0.600 2025.01.27 188de P P 1458 550 2025.01.27 23:05:21.162 2025.01.27 23:05:22.620 2025.01.27 21:26:01.027 2025.01.27 21:26:01.577
83 6.0.0.599 2025.01.25 ba588 P P 1584 595 2025.01.25 23:06:02.511 2025.01.25 23:06:04.095 2025.01.25 21:26:16.345 2025.01.25 21:26:16.940
84 6.0.0.598 2025.01.23 ddbc3 P P 1403 558 2025.01.24 23:05:21.724 2025.01.24 23:05:23.127 2025.01.24 21:26:02.844 2025.01.24 21:26:03.402
85 6.0.0.595 2025.01.22 e62f3 P P 1211 490 2025.01.22 22:59:14.023 2025.01.22 22:59:15.234 2025.01.22 21:25:44.979 2025.01.22 21:25:45.469
86 6.0.0.594 2025.01.21 47fb6 P P 1286 518 2025.01.21 22:58:48.741 2025.01.21 22:58:50.027 2025.01.21 21:25:41.001 2025.01.21 21:25:41.519
87 6.0.0.590 2025.01.20 9dc1e P P 1388 469 2025.01.20 23:04:37.795 2025.01.20 23:04:39.183 2025.01.20 21:25:55.552 2025.01.20 21:25:56.021
88 6.0.0.588 2025.01.19 b1c4e P P 1256 491 2025.01.19 23:01:11.098 2025.01.19 23:01:12.354 2025.01.19 21:25:57.878 2025.01.19 21:25:58.369
89 6.0.0.587 2025.01.18 63e6e P P 1162 595 2025.01.18 23:03:52.353 2025.01.18 23:03:53.515 2025.01.18 21:26:24.387 2025.01.18 21:26:24.982
90 6.0.0.585 2025.01.16 2d6bb P P 1237 499 2025.01.17 22:58:44.948 2025.01.17 22:58:46.185 2025.01.17 21:25:37.800 2025.01.17 21:25:38.299
91 6.0.0.584 2025.01.15 a0aa2 P P 1130 595 2025.01.15 23:02:50.995 2025.01.15 23:02:52.125 2025.01.15 21:25:54.158 2025.01.15 21:25:54.753
92 6.0.0.581 2025.01.14 21e9e P P 1365 585 2025.01.14 23:03:47.694 2025.01.14 23:03:49.059 2025.01.14 21:26:03.003 2025.01.14 21:26:03.588
93 6.0.0.577 2025.01.13 7e293 P P 1329 557 2025.01.13 23:04:25.149 2025.01.13 23:04:26.478 2025.01.13 21:25:59.774 2025.01.13 21:26:00.331
94 6.0.0.576 2025.01.12 05898 P P 1374 511 2025.01.12 23:01:44.377 2025.01.12 23:01:45.751 2025.01.12 21:25:45.417 2025.01.12 21:25:45.928
95 6.0.0.573 2025.01.10 c20f3 P P 1311 554 2025.01.10 23:02:27.015 2025.01.10 23:02:28.326 2025.01.10 21:25:56.564 2025.01.10 21:25:57.118
96 6.0.0.571 2024.12.31 81bba P P 975 427 2024.12.31 22:39:08.279 2024.12.31 22:39:09.254 2024.12.31 21:23:33.447 2024.12.31 21:23:33.874
97 6.0.0.570 2024.12.30 c3c8d P P 1072 379 2024.12.30 22:38:20.005 2024.12.30 22:38:21.077 2024.12.30 21:23:37.633 2024.12.30 21:23:38.012
98 6.0.0.565 2024.12.28 5fc59 P P 660 415 2024.12.30 11:39:30.021 2024.12.30 11:39:30.681 2024.12.30 10:32:01.803 2024.12.30 10:32:02.218
99 6.0.0.564 2024.12.26 12514 P P 1222 410 2024.12.26 22:40:00.847 2024.12.26 22:40:02.069 2024.12.26 21:23:35.202 2024.12.26 21:23:35.612
100 6.0.0.560 2024.12.25 fa83e P P 1093 385 2024.12.25 22:39:38.141 2024.12.25 22:39:39.234 2024.12.25 21:23:40.465 2024.12.25 21:23:40.850
101 6.0.0.559 2024.12.23 cc800 P P 1121 422 2024.12.24 22:41:52.757 2024.12.24 22:41:53.878 2024.12.24 21:23:30.534 2024.12.24 21:23:30.956
102 6.0.0.556 2024.12.22 a0404 P P 1136 398 2024.12.22 22:39:41.187 2024.12.22 22:39:42.323 2024.12.22 21:23:28.537 2024.12.22 21:23:28.935
103 6.0.0.555 2024.12.19 6990a P P 628 395 2024.12.21 12:15:58.675 2024.12.21 12:15:59.303 2024.12.21 10:58:59.605 2024.12.21 10:59:00.000
104 6.0.0.553 2024.12.17 d1f8a P P 1088 440 2024.12.17 22:41:01.498 2024.12.17 22:41:02.586 2024.12.17 21:23:51.682 2024.12.17 21:23:52.122
105 6.0.0.552 2024.12.11 85e25 P P 1163 387 2024.12.15 22:40:29.218 2024.12.15 22:40:30.381 2024.12.15 21:23:36.250 2024.12.15 21:23:36.637
106 6.0.0.550 2024.12.10 b37ac P P 693 411 2024.12.10 22:43:31.442 2024.12.10 22:43:32.135 2024.12.10 21:23:50.066 2024.12.10 21:23:50.477
107 6.0.0.548 2024.12.08 2cc77 P P 1222 365 2024.12.08 22:37:24.429 2024.12.08 22:37:25.651 2024.12.08 21:23:35.823 2024.12.08 21:23:36.188
108 6.0.0.544 2024.12.05 96943 P P 1124 439 2024.12.05 22:42:22.258 2024.12.05 22:42:23.382 2024.12.05 21:24:09.570 2024.12.05 21:24:10.009
109 6.0.0.543 2024.12.03 30b77 P P 1146 391 2024.12.03 22:41:57.489 2024.12.03 22:41:58.635 2024.12.03 21:23:52.329 2024.12.03 21:23:52.720
110 6.0.0.540 2024.12.02 4a1f4 P P 981 407 2024.12.02 22:36:15.638 2024.12.02 22:36:16.619 2024.12.02 21:23:39.230 2024.12.02 21:23:39.637
111 6.0.0.539 2024.11.28 1f283 P P 1191 388 2024.11.29 22:40:52.439 2024.11.29 22:40:53.630 2024.11.29 21:23:47.599 2024.11.29 21:23:47.987
112 6.0.0.535 2024.11.26 77b95 P P 1076 369 2024.11.26 22:33:15.087 2024.11.26 22:33:16.163 2024.11.26 21:22:56.428 2024.11.26 21:22:56.797
113 6.0.0.534 2024.11.25 e9584 P P 654 428 2024.11.25 22:34:16.461 2024.11.25 22:34:17.115 2024.11.25 21:22:59.259 2024.11.25 21:22:59.687
114 6.0.0.533 2024.11.17 933ac P P 663 390 2024.11.22 08:56:30.634 2024.11.22 08:56:31.297 2024.11.22 07:52:24.394 2024.11.22 07:52:24.784
115 6.0.0.532 2024.11.16 9e263 P P 1118 397 2024.11.16 22:30:42.382 2024.11.16 22:30:43.500 2024.11.16 21:23:14.635 2024.11.16 21:23:15.032
116 6.0.0.530 2024.11.15 49804 P P 1011 403 2024.11.16 00:34:09.782 2024.11.16 00:34:10.793 2024.11.15 23:23:04.421 2024.11.15 23:23:04.824
117 6.0.0.528 2024.11.14 9625b P P 636 397 2024.11.15 00:35:05.120 2024.11.15 00:35:05.756 2024.11.14 23:23:20.162 2024.11.14 23:23:20.559
118 6.0.0.526 2024.11.12 65b80 P P 654 396 2024.11.14 00:34:49.054 2024.11.14 00:34:49.708 2024.11.13 23:23:03.165 2024.11.13 23:23:03.561
119 6.0.0.523 2024.11.08 8ca23 P P 1084 418 2024.11.11 00:33:09.447 2024.11.11 00:33:10.531 2024.11.10 23:22:47.450 2024.11.10 23:22:47.868
120 6.0.0.520 2024.11.07 4eefa P P 1029 370 2024.11.08 00:31:53.846 2024.11.08 00:31:54.875 2024.11.07 23:22:52.110 2024.11.07 23:22:52.480
121 6.0.0.516 2024.11.04 b0c36 P P 582 393 2024.11.05 00:31:33.882 2024.11.05 00:31:34.464 2024.11.04 23:22:49.389 2024.11.04 23:22:49.782
122 6.0.0.515 2024.10.30 d53f3 P P 1100 382 2024.11.04 00:32:39.226 2024.11.04 00:32:40.326 2024.11.03 23:22:50.463 2024.11.03 23:22:50.845
123 6.0.0.512 2024.10.29 833ef P P 664 458 2024.10.30 00:34:01.002 2024.10.30 00:34:01.666 2024.10.29 23:23:22.244 2024.10.29 23:23:22.702
124 6.0.0.511 2024.10.26 c4bc9 P P 640 413 2024.10.29 00:34:05.714 2024.10.29 00:34:06.354 2024.10.28 23:22:52.264 2024.10.28 23:22:52.677
125 6.0.0.509 2024.10.25 3aedb P P 1151 398 2024.10.26 00:39:12.568 2024.10.26 00:39:13.719 2024.10.25 23:23:41.770 2024.10.25 23:23:42.168
126 6.0.0.508 2024.10.24 a8f5b P P 960 464 2024.10.25 00:35:44.341 2024.10.25 00:35:45.301 2024.10.24 23:23:34.837 2024.10.24 23:23:35.301
127 6.0.0.502 2024.10.22 6bfd7 P P 650 423 2024.10.23 00:35:23.029 2024.10.23 00:35:23.679 2024.10.22 23:23:22.023 2024.10.22 23:23:22.446
128 6.0.0.500 2024.10.21 be565 P P 726 416 2024.10.22 16:58:42.265 2024.10.22 16:58:42.991 2024.10.22 15:52:34.783 2024.10.22 15:52:35.199
129 6.0.0.499 2024.10.19 6214b P P 1073 464 2024.10.20 00:36:15.228 2024.10.20 00:36:16.301 2024.10.19 23:23:51.543 2024.10.19 23:23:52.007
130 6.0.0.498 2024.10.18 591a7 P P 662 401 2024.10.19 00:34:47.585 2024.10.19 00:34:48.247 2024.10.18 23:23:14.977 2024.10.18 23:23:15.378
131 6.0.0.494 2024.10.17 cf5a4 P P 685 440 2024.10.18 00:35:32.468 2024.10.18 00:35:33.153 2024.10.17 23:23:51.327 2024.10.17 23:23:51.767
132 6.0.0.491 2024.10.14 dc5fb P P 955 434 2024.10.15 00:33:23.635 2024.10.15 00:33:24.590 2024.10.14 23:23:01.194 2024.10.14 23:23:01.628
133 6.0.0.489 2024.10.11 2ba59 P P 1126 417 2024.10.12 00:36:44.285 2024.10.12 00:36:45.411 2024.10.11 23:23:25.696 2024.10.11 23:23:26.113
134 6.0.0.488 2024.10.09 1c93e P P 1336 408 2024.10.10 00:42:39.910 2024.10.10 00:42:41.246 2024.10.09 23:24:04.935 2024.10.09 23:24:05.343
135 6.0.0.487 2024.10.06 065a3 P P 1227 433 2024.10.07 00:41:47.750 2024.10.07 00:41:48.977 2024.10.06 23:24:12.217 2024.10.06 23:24:12.650
136 6.0.0.485 2024.10.04 e95c1 P P 721 491 2024.10.05 00:43:27.056 2024.10.05 00:43:27.777 2024.10.04 23:24:01.391 2024.10.04 23:24:01.882
137 6.0.0.483 2024.10.02 5e5ae P P 1328 412 2024.10.03 00:40:58.766 2024.10.03 00:41:00.094 2024.10.02 23:23:51.076 2024.10.02 23:23:51.488
138 6.0.0.478 2024.09.30 b5010 P P 1164 444 2024.10.01 00:38:44.481 2024.10.01 00:38:45.645 2024.09.30 23:23:42.972 2024.09.30 23:23:43.416
139 6.0.0.474 2024.09.26 e4efb P P 837 386 2024.09.30 00:36:22.122 2024.09.30 00:36:22.959 2024.09.29 23:23:23.665 2024.09.29 23:23:24.051
140 6.0.0.471 2024.09.24 01b51 P P 1040 397 2024.09.25 22:38:26.134 2024.09.25 22:38:27.174 2024.09.25 21:23:25.999 2024.09.25 21:23:26.396
141 6.0.0.470 2024.09.23 77cc0 P P 669 460 2024.09.23 22:38:02.562 2024.09.23 22:38:03.231 2024.09.23 21:23:41.866 2024.09.23 21:23:42.326
142 6.0.0.467 2024.09.21 ea0b8 P P 1003 420 2024.09.22 22:37:08.479 2024.09.22 22:37:09.482 2024.09.22 21:23:16.186 2024.09.22 21:23:16.606
143 6.0.0.466 2024.09.20 32dc6 P P 573 428 2024.09.20 22:36:19.115 2024.09.20 22:36:19.688 2024.09.20 21:23:17.450 2024.09.20 21:23:17.878
144 6.0.0.461 2024.09.17 2c895 P P 1305 719 2024.09.17 23:02:32.544 2024.09.17 23:02:33.849 2024.09.17 21:27:05.746 2024.09.17 21:27:06.465
145 6.0.0.460 2024.09.11 3c253 P P 1506 762 2024.09.16 23:04:05.566 2024.09.16 23:04:07.072 2024.09.16 21:27:02.089 2024.09.16 21:27:02.851
146 6.0.0.457 2024.09.09 fdc6f P P 1695 757 2024.09.09 23:18:03.909 2024.09.09 23:18:05.604 2024.09.09 21:28:04.880 2024.09.09 21:28:05.637
147 6.0.0.455 2024.09.07 500d8 P P 1450 752 2024.09.07 23:15:45.112 2024.09.07 23:15:46.562 2024.09.07 21:28:19.472 2024.09.07 21:28:20.224
148 6.0.0.454 2024.09.05 4d70f P P 1384 730 2024.09.05 23:04:21.172 2024.09.05 23:04:22.556 2024.09.05 21:27:38.614 2024.09.05 21:27:39.344
149 6.0.0.452 2024.09.04 9ff9c P P 1347 779 2024.09.04 23:03:19.005 2024.09.04 23:03:20.352 2024.09.04 21:27:16.003 2024.09.04 21:27:16.782
150 6.0.0.450 2024.09.02 27124 P P 1792 1177 2024.09.03 23:42:00.226 2024.09.03 23:42:02.018 2024.09.03 21:37:08.727 2024.09.03 21:37:09.904
151 6.0.0.447 2024.09.01 056ec P P 2906 720 2024.09.01 23:15:30.465 2024.09.01 23:15:33.371 2024.09.01 21:27:04.377 2024.09.01 21:27:05.097
152 6.0.0.446 2024.08.30 fe1b2 P P 1093 1349 2024.08.31 23:20:06.266 2024.08.31 23:20:07.359 2024.08.31 21:36:13.422 2024.08.31 21:36:14.771
153 6.0.0.444 2024.08.28 785d4 P P 2322 1742 2024.08.29 23:35:40.576 2024.08.29 23:35:42.898 2024.08.29 21:36:00.640 2024.08.29 21:36:02.382
154 6.0.0.442 2024.08.21 4a68f P P 1120 868 2024.08.28 00:34:21.126 2024.08.28 00:34:22.246 2024.08.27 21:42:23.684 2024.08.27 21:42:24.552
155 6.0.0.441 2024.08.20 75042 P P 1635 807 2024.08.20 23:08:41.311 2024.08.20 23:08:42.946 2024.08.20 21:27:56.589 2024.08.20 21:27:57.396
156 6.0.0.438 2024.08.16 088b5 P P 1301 478 2024.08.18 22:42:05.583 2024.08.18 22:42:06.884 2024.08.18 21:24:45.994 2024.08.18 21:24:46.472
157 6.0.0.437 2024.08.14 3c88b P P 1372 557 2024.08.15 22:40:40.973 2024.08.15 22:40:42.345 2024.08.15 21:24:47.626 2024.08.15 21:24:48.183
158 6.0.0.432 2024.08.11 e82ac P P 1346 578 2024.08.12 22:39:24.243 2024.08.12 22:39:25.589 2024.08.12 21:24:35.687 2024.08.12 21:24:36.265
159 6.0.0.431 2024.08.09 de5a7 P P 1359 543 2024.08.09 22:40:08.597 2024.08.09 22:40:09.956 2024.08.09 21:25:00.562 2024.08.09 21:25:01.105
160 6.0.0.428 2024.08.08 9191b P P 1224 603 2024.08.08 22:39:21.100 2024.08.08 22:39:22.324 2024.08.08 21:24:53.460 2024.08.08 21:24:54.063
161 6.0.0.423 2024.08.07 33b41 P P 790 501 2024.08.08 08:41:07.684 2024.08.08 08:41:08.474 2024.08.08 07:32:26.273 2024.08.08 07:32:26.774
162 6.0.0.421 2024.08.06 ed60d P P 849 371 2024.08.06 22:29:35.513 2024.08.06 22:29:36.362 2024.08.06 21:23:27.305 2024.08.06 21:23:27.676
163 6.0.0.419 2024.08.05 3505a P P 926 319 2024.08.05 22:31:44.529 2024.08.05 22:31:45.455 2024.08.05 21:23:39.194 2024.08.05 21:23:39.513
164 6.0.0.409 2024.08.02 ec18f P P 927 343 2024.08.04 22:33:28.630 2024.08.04 22:33:29.557 2024.08.04 21:23:48.363 2024.08.04 21:23:48.706
165 6.0.0.406 2024.08.01 b20be P P 912 324 2024.08.01 22:32:35.881 2024.08.01 22:32:36.793 2024.08.01 21:23:35.716 2024.08.01 21:23:36.040
166 6.0.0.405 2024.07.31 a62ac P P 833 432 2024.07.31 22:32:11.057 2024.07.31 22:32:11.890 2024.07.31 21:23:40.533 2024.07.31 21:23:40.965
167 6.0.0.403 2024.07.29 30f03 P P 876 414 2024.07.29 22:31:51.329 2024.07.29 22:31:52.205 2024.07.29 21:23:24.490 2024.07.29 21:23:24.904
168 6.0.0.401 2024.07.26 24e41 P P 854 350 2024.07.26 22:29:15.808 2024.07.26 22:29:16.662 2024.07.26 21:23:24.668 2024.07.26 21:23:25.018
169 6.0.0.400 2024.07.24 5bb78 P P 936 435 2024.07.24 22:29:20.145 2024.07.24 22:29:21.081 2024.07.24 21:23:27.588 2024.07.24 21:23:28.023
170 6.0.0.398 2024.07.23 85b18 P P 696 402 2024.07.23 22:34:15.173 2024.07.23 22:34:15.869 2024.07.23 21:23:27.289 2024.07.23 21:23:27.691
171 6.0.0.397 2024.07.22 c734c P P 1007 415 2024.07.22 22:29:10.356 2024.07.22 22:29:11.363 2024.07.22 21:23:25.040 2024.07.22 21:23:25.455
172 6.0.0.396 2024.07.13 cf952 P P 904 352 2024.07.21 22:28:57.910 2024.07.21 22:28:58.814 2024.07.21 21:23:10.148 2024.07.21 21:23:10.500
173 6.0.0.395 2024.07.10 845f4 P P 1024 381 2024.07.12 22:28:20.511 2024.07.12 22:28:21.535 2024.07.12 21:23:04.670 2024.07.12 21:23:05.051
174 6.0.0.392 2024.07.09 ea301 P P 940 395 2024.07.09 22:26:26.622 2024.07.09 22:26:27.562 2024.07.09 21:23:03.841 2024.07.09 21:23:04.236
175 6.0.0.391 2024.07.08 7d50c P P 944 412 2024.07.08 22:27:50.505 2024.07.08 22:27:51.449 2024.07.08 21:23:17.675 2024.07.08 21:23:18.087
176 6.0.0.389 2024.07.05 cc71c P P 946 325 2024.07.05 22:28:08.633 2024.07.05 22:28:09.579 2024.07.05 21:23:12.444 2024.07.05 21:23:12.769
177 6.0.0.388 2024.06.30 e5700 P P 966 405 2024.06.30 22:27:47.442 2024.06.30 22:27:48.408 2024.06.30 21:23:16.060 2024.06.30 21:23:16.465
178 6.0.0.387 2024.06.27 7c28a P P 897 374 2024.06.27 22:27:48.028 2024.06.27 22:27:48.925 2024.06.27 21:23:18.885 2024.06.27 21:23:19.259
179 6.0.0.386 2024.06.23 7c57f P P 900 388 2024.06.23 22:25:37.145 2024.06.23 22:25:38.045 2024.06.23 21:22:57.563 2024.06.23 21:22:57.951
180 6.0.0.384 2024.06.21 24d99 P P 836 403 2024.06.21 22:28:10.605 2024.06.21 22:28:11.441 2024.06.21 21:22:58.041 2024.06.21 21:22:58.444
181 6.0.0.374 2024.06.13 0097d P P 1038 370 2024.06.20 22:27:53.193 2024.06.20 22:27:54.231 2024.06.20 21:22:55.623 2024.06.20 21:22:55.993
182 6.0.0.373 2024.06.09 363f0 P P 572 385 2024.06.13 12:47:07.060 2024.06.13 12:47:07.632 2024.06.13 11:46:08.528 2024.06.13 11:46:08.913
183 6.0.0.371 2024.06.08 f7130 P P 553 330 2024.06.11 21:39:19.191 2024.06.11 21:39:19.744 2024.06.11 20:44:47.125 2024.06.11 20:44:47.455
184 6.0.0.366 2024.05.30 ab2c9 P P 997 400 2024.06.12 08:22:30.556 2024.06.12 08:22:31.553 2024.06.12 07:16:28.690 2024.06.12 07:16:29.090
185 6.0.0.363 2024.05.27 06703 P P 585 398 2024.06.12 13:44:39.953 2024.06.12 13:44:40.538 2024.06.12 12:48:02.112 2024.06.12 12:48:02.510
186 6.0.0.359 2024.05.23 9cb11 P P 567 302 2024.06.12 11:37:00.656 2024.06.12 11:37:01.223 2024.06.12 10:40:10.350 2024.06.12 10:40:10.652
187 6.0.0.358 2024.05.21 995dd P P 526 311 2024.06.12 16:21:18.506 2024.06.12 16:21:19.032 2024.06.12 15:25:19.649 2024.06.12 15:25:19.960
188 6.0.0.357 2024.05.18 bf6c4 P P 506 374 2024.06.12 20:57:27.340 2024.06.12 20:57:27.846 2024.06.12 20:01:46.688 2024.06.12 20:01:47.062
189 6.0.0.356 2024.05.17 eab06 P P 540 340 2024.06.13 05:04:08.377 2024.06.13 05:04:08.917 2024.06.13 04:07:53.379 2024.06.13 04:07:53.719
190 6.0.0.355 2024.05.16 8dd6e P P 566 317 2024.06.13 07:11:10.572 2024.06.13 07:11:11.138 2024.06.13 06:15:19.121 2024.06.13 06:15:19.438
191 6.0.0.354 2024.05.15 d3adc P P 606 391 2024.06.13 10:12:49.431 2024.06.13 10:12:50.037 2024.06.13 09:11:23.349 2024.06.13 09:11:23.740
192 6.0.0.351 2024.05.14 2e3e0 P P 588 393 2024.06.13 15:26:07.737 2024.06.13 15:26:08.325 2024.06.13 14:24:05.034 2024.06.13 14:24:05.427

Elapsed time, ms. Chart for last 150 runs:

Last commits information (all timestamps in UTC):